globalsymboling issues

$Adam's icon

Dear Developers,

here's a strange scenario which I don't understand completely: I have an external (let's call it [server]) that calls globalsymbol_bind in its new object method to bind itself to a name and then calls globalsymbol_unbind to unbind from that name. I have another external (let's call it [client]) that is trying to get a reference to the first object by calling globalsymbol_reference. Now, here is the strange thing. If I follow this process:

1) Create two instances of [server] (one after the other) with the same name
2) Delete the [server] that I created first
3) Create a [client] and assign it to the name of the server

then when I call globalsymbol_reference, I get a NULL, although there is still a server with the same name in the patcher.

If I create the [client] before creating the [server] objects, then I can hack the thing by dealing with the 'globalsymbol_bind' and 'globalsymbol_unbind' notifications which would be sent when a [server] is bound/unbound to the given name. The problem here is that when I create the [client] as a last step in the above procedure, there's no way to find out whether there is still a server which is alive or not.

Do you have any ideas on how to solve this issue?

Thanks,
Ádám

$Adam's icon

For those that might be interested in this topic, I describe the hacking I finally did.

Obviously (although this is not documented anywhere) it seems that, if more than one call occurs to globalsymbol_bind with the same name and namespace parameters, then, although the registered objects would still get all notifications from all bound servers, the s_thing belonging to the registered name will be set to NULL (and therefore globalsymbol_reference, although will still register the object to the server and therefore the [client] would get all notifications sent by the [server], will return NULL instead of a valid pointer to a [server] object). Might this be actually a bug? I don't know...

However, if only one [server] is bound at the same time to the given name, then globalsymbol_reference will always return a pointer to the uniquely registered object. So a solution (a hack?) might be (at least this is what I implemented) if one creates a global (static) hash table that keeps track of all [server] objects and all their names and adds a boolean field to the t_object of the [server] which will be set to true when the actual server is bount to a name and to false otherwise (in the new object method one should check the hash table if the [server]'s desired name is already registered and, if not, it can call globalsymbol_bind using the desired name and the bool can be set to true). When the actual [server] that was bound to the name is being disposed, it should first call the globalsymbol_unbind and then it should check if there is a [server] in the hash table with the same name. If yes, then it should call globalsymbol_bind using the still alive [server] object from the hash table.

The benefit of this is that when one calls the globalsymbol_bind/unbind messages, the registered clients will get the globalsymbol_binding/unbinding notifications and therefore they can update their references automatically to the newly bound [server] objects just by implementing a proper response to these two messages.

Although this method works, it really doesn't feel comfortable, and I'm not sure if I really understood the purpose of the globalsymbol_* stuff, or if this is some huge misinterpretation of this whole thing. Actually I though that the hash table stuff would be automatically handled by the Max API itself and the proper registrations and name binding would occur silently in the background. I'm still not sure if this is actually the case just I didn't understand something or if this is a buggy behaviour of the globalsymbol_* calls.

Anyway, I hope this might still be helpful for someone in the future...
Ádám

$Adam's icon

One more comment. The whole hashtabling thing can be done quite easily if one writes the code in C++ instead of C. In this case, the global static variable which you need would simply be:

static std::map < t_symbol *, std::set < void * > > serverMap;

Of course the void *s would actually always be t_object *s, but since the data field of your notify method (the one that would catch the globalsymbol_binding/unbinding messages) is a void *, it is easier to use the same type in your hash table.

Ádám

Joshua Kit Clayton's icon

Hi Adam,

Your workaround is appropriate for working with globalsymbol which was built for an older strategy that buffer and coll use. Alternately, you can use object_register which does take into account the name and will free the second incoming object for a double register and work as you expect with client objects. However, object_register will not perform the other features for which globalsymbol is used:

1. assign the object to the global s_thing pointer.
2. notify objects waiting for an object to register.

So, because of #2, you need to use a lazy form of attachment which asks if a server is present at some time when trying to work with the server. It can not attach before the server exists. This is a nuisance, so in the next major version of max, there is an object_subscribe() call which permits "attaching" to object_register() servers which do not yet exist.

Sorry for the inconvenience. This is precisely the situation I described when I mentioned that neither globalsymbol, nor object_register gives you everything you need. As you can tell, it's not terribly difficult to workaround either globalsymbol or object_register limitations.

$Adam's icon

Hi Joshua,

thanks for confirming. Actually now everything works quite fine and seems to be robust with this workaround. I'll keep using it until I get the notify for the next major version. :-)