globalsymboling issues
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
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
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
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.
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. :-)