Problem with waiting for a service to be available
Randell Jesup
rjesup at wgate.com
Wed Sep 23 14:03:06 PDT 2009
For any on the dbus-cplusplus list, you can look at the dbus archives to
see the earlier messages here. Note that the example below assumes the
patch to the dbus-c++ git tree for request_name() return values I
emailed to the list a week ago, though I don't think I've seen it appear
yet...
>>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).
Copied from below:
>The alternative (haven't tried it yet) is to put "FooService server(conn);"
>right before the "try {}" section, and dispense with server.Ready().
Note: at least in dbus-c++, the above does NOT work. Even with creating
the FooService server object before request_name(), the first method
from someone watching NameOwnerChanged is delayed by ~25 seconds.
If the watcher waits 1 second after NameOwnerChanged or waits for
Ready() (per the example below), then everything works fast.
In dbus-monitor with --profile, I see that when the delay happens, the
AddMatch submitted by dbus-c++ following successful request_name()
occurs after the "watching" process has tried to send a method. Eg:
(cleaned up for easier viewing)
sig 1253738585 /org/freedesktop/DBus org.freedesktop.DBus NameOwnerChanged
mc 1253738585 :1.17 /org/freedesktop/DBus org.freedesktop.DBus RequestName
mc 1253738585 :1.16 /com/foo/FooService com.foo.FooService init
mc 1253738585 :1.17 /org/freedesktop/DBus org.freedesktop.DBus AddMatch
mc 1253738610 :1.16 /com/foo/FooService com.foo.FooService version
Note the 25-second delay (and FooService doesn't see "init()" until the
end of those 25 seconds), and AddMatch is after init(). Note that
AddMatch is for FooService:
method call sender=:1.17 -> dest=org.freedesktop.DBus path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=AddMatch
string "destination='com.foo.FooService'"
If I add a 1-second delay to the watcher, you get this:
sig 1253738755 /org/freedesktop/DBus org.freedesktop.DBus NameOwnerChanged
mc 1253738755 :1.19 /org/freedesktop/DBus org.freedesktop.DBus RequestName
mc 1253738755 :1.19 /org/freedesktop/DBus org.freedesktop.DBus AddMatch
mc 1253738756 :1.18 /com/foo/FooService com.foo.FooService init
mc 1253738757 :1.18 /com/foo/FooService com.foo.FooService version
Note that AddMatch happens first, and no 25-second delay.
This may well be a hole in dbus-c++, however it feels like it might be a
race condition in the protocol itself - i.e. is the AddMatch after
dbus_bus_request_name needed in order to receive methods for that path/name?
>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
--
Randell Jesup, Worldgate
rjesup at wgate.com
More information about the dbus
mailing list