GBus discussion

Havoc Pennington hp at redhat.com
Fri Aug 17 13:51:49 PDT 2007


Hi,

Fan Wu wrote:
> If that is the case you can go further by getting rid of all the
> register/unregister calls, and ask user to fill in a big table which
> has all the callback/type/path/name information, and then pass the
> table to a dbus_start() which will set up the loop and get the
> connection and call the callbacks if needed.

It's worth exploring, but I would build it on the register/unregister 
primitives for individual things, rather than getting rid of those. The 
current way looks like:

  register_connection_handler(connected_handler, disconnected_handler)

  connected_handler(Connection *cnxn)
  {
     register_bus_name_owner(cnxn, name_owned_handler, 
name_not_owned_handler)
  }

  disconnected_handler(Connection *cnxn)
  {
   unregister_bus_name_owner(cnxn, name_owned_handler, 
name_not_owned_handler);
  }

  name_owned_handler(Connection *cnxn)
  {
     register_relevant_signals_and_objects(cnxn)
  }

  name_not_owned_handler(Connection *cnxn)
  {
     unregister_relevant_signals_and_objects(cnxn)
  }

Note that for this to work well, when the connection is disconnected you 
would call the callbacks in reverse order from when connecting:
  - first mark all the names not owned and call those callbacks
  - next call disconnected handler

I think this is pretty good, but as you say can be improved.

But even in the code already in the gbus directory, there are some 
"multiple steps at once" for example the single_instance functions 
register a connection handler which on connect registers a bus name 
owner which on getting the name finally calls into the app.

So already there's an example of building a higher-level primitive that 
hides several nested callbacks from the app - the app only has to write 
the owned/not_owned handlers.

There's room to combine more steps and avoid intermediate callbacks that 
would contain only further registration calls.

I would keep the register/unregister API in any case, but then build 
additional layers on top of it that do register/unregister "chains" such 
as the single instance chain from connected->name owned->install objects

>> The point is that you could register multiple NameOwner objects and
>> data for the same owned_name. So only the first one matching the
>> passed-in NameOwner and data would be unregistered.
> 
> Why would you need multiple NameOwner objects for one name?

Think of name acquired/lost as a signal; you might have two callbacks in 
different parts of the code that want to do something when losing a 
particular name.

> The difference between NameLost and FailedToAcquire is, for NameLost
> most likely something needs to be cleaned up before exit; while for
> FailedToAcquire, probably nothing has been started yet so the clean up
> can be saved. I would say some application might appreciate the extra
> information, if just for logging purpose.

Remember that we might also have a sequence like this:

  1. connect
  2. request name
  3. got name, call name-owned callback
  4. disconnected, call name-not-owned callback
  5. reconnect
  6. request name
  7. did not get name, call name-not-owned callback

So the state the app would need to know in this scenario is whether it 
had ever gotten a "name owned" - the app does not care _how_ the library 
knows that the name is not owned.

You could put a "dbus_bool_t first_time" flag in the callback, but I 
would advise against it, because as a rule having any 
not-absolutely-essential args to callbacks makes them more annoying and 
less convenient. The app can always store what it needs to know in the 
application data structure (which is the callback data pointer). In this 
case, for example, the app would generally be written like:

typedef struct { dbus_bool_t initialized; } Application

main()
{
   Application *app = app_new();
   register_single_instance(owned_callback, not_owned_callback, app);
}

void
owned_callback(DBusConnection *connection,
                void           *data)
{
   Application *app = data;
   if (!app->initialized)
    {
      app_initialize(app);
    }
}

void
not_owned_callback(DBusConnection *connection,
                    void           *data)
{
   Application *app = data;
   if (app->initialized)
    {
      app_uninitialize(app);
    }
}

I would suggest guaranteeing in the API docs that these two callbacks 
are always called alternating (you never get two "owned" in a row or two 
"not owned" in a row). However, if you guarantee that owned_callback is 
always called first, I think there's nothing good to do if the name is 
never owned once.

Havoc



More information about the dbus mailing list