Problem with waiting for a service to be available
Randell Jesup
rjesup at wgate.com
Tue Sep 22 14:05:19 PDT 2009
>>There seems to be a designed-in timing hole in the DBus architecture, or
>>perhaps it isn't obvious (or explained) how you're supposed to wait for
>>a service to be available. Note that "ServiceName" is using dbus-c++
>>(the working "git" tree branch with ecore integration). Perhaps even
>>it's a dbus-c++ issue, though it doesn't seem like it.
>>
>>
>>If (like Qt's wrapper does to update ::isValid()) your application
>>watches for NameOwnerChanged to know when a destination is available to
>>send methods to, the ordering of operations leaves a timing hole. To
>>whit:
>
>Just to reiterate and verify I'm not (or dbus-c++ isn't) doing something
>wrong:
>
>When I receive signal "NameOwnerChanged" for ServiceName, if I send a
>method to ServiceName *immediately* will it arrive there reliably (and
>quickly)? I'm seeing a 15-30 second delay (seems like a retry within
>the dbus daemon) from when I send the method and the recipient appears
>to receive it. I know it's not queuing within Qt since dbus-monitor
>shows the init method being sent immediately.
Ok, I've resolved the problem. There is a bit of a hole if you're not
careful - dbus doesn't really protect you from it, and while a binding
can, I suspect many of the them don't (include dbus-c++).
The problem is that dbus-bus_request_name() will notify listeners that
you own the name, but until you've created handlers for objects under
that name people won't be able to successfully send you messages (though
in practice, it appears that they're retried before giving up (15-30
seconds later), and so by then the handlers exist). If they're just
itching to send you a message, they can beat the creation of the server
object(s).
In order to avoid all timing holes, you'd need to internally initialize
all objects your entity supports before requesting the name. Most
sample code in dbus wrappers doesn't do this: it requests the name, then
instantiates the object. (Or at least so it seemed looking around the net).
The other way to deal with it is to add an application-level protocol on
top of DBus, like a "Ready()" signal raised when your service is ready
to handle all requests. Ugly but simple. Example:
>This is the dbus-c++ code for aquiring the name (assumes a patch to
>dbus-c++ I sent in a few days ago to get the return code from
>dbus_bus_request_name()):
>
> DBus::Connection conn = DBus::Connection::SessionBus();
> // Make sure we never get two instances running!
> try
> {
> int result;
> syslog(LOG_INFO,"requesting name");
> if ((result = conn.request_name(SERVER_NAME,DBUS_NAME_FLAG_DO_NOT_QUEUE)) !=
> DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER)
> {
> syslog(LOG_ERR,"Request_name returned %d",result);
> ecore_shutdown();
> return 1;
> }
> }
> catch(DBus::Error &e)
> {
> syslog(LOG_ERR,"Error: Request name failure: %s",e.message());
> ecore_shutdown();
> return 1;
> }
FooService server(conn);
server.Ready(); // tell the world - avoids races on ownership of the name
The alternative (haven't tried it yet) is to put "FooService server(conn);"
right before the "try {}" section, and dispense with server.Ready().
--
Randell Jesup, Worldgate (developers of the Ojo videophone)
rjesup at wgate.com
More information about the dbus
mailing list