GBus discussion

Havoc Pennington hp at redhat.com
Fri Aug 17 11:25:09 PDT 2007


[resend, forgot to reply all]

Hi,

One overall thing, I think there's another project called gbus also,
though either there hasn't been stuff about it on this list or I
missed it somehow. So, we may need a new name (especially if doing
something that doesn't use glib)

On 8/17/07, Fan Wu <wufan9418 at gmail.com> wrote:
> Just to make sure I understand it correctly, the eventual goals of gbus are:
>
> * a thin wrapper of libdbus API which is shipped as part of libdbus
> * no dependency on glib
> * agnostic to looping mechanism (works with both thread and mainloop)

Well, the goals I had for this sketch were to require the GLib main
loop. We can change that to no dependency on GLib, but some
convenience is given up.
Perhaps, though, a "GLib binding" designed to accompany this library
could be _only_ the single "setup main loop" call.

Or we could do the thing discussed in another recent thread and have:

typedef struct {
 const char *module_name;
 void* (* DBusFrameworkInit) (void); // returns framework_data
 void (* DBusFrameworkShutdown) (void* framework_data);
 void (* DBusFrameworkNewConnection) (DBusConnection *,
                                                            void
*framework_data)
 void (* DBusFrameworkNewServer) (DBusServer *,
                                                     void *framework_data);
 void (* DBusFrameworkIdleAdd) (void (*callback)(void *data), void *data,
                                                void *framework_data);
 void (* DBusFrameworkIdleRemove)(callback, data, framework_data);
 void (* private_future_expansion)(void *framework_data);
 void (* private_future_expansion)(void *framework_data);
 void (* private_future_expansion)(void *framework_data);
 void (* private_future_expansion)(void *framework_data);
} DBusFrameworkModule;

dbus_install_framework(DBusFrameworkModule *module);

  (I would make framework_data the first arg to everything, but don't
  feel like retyping it)

So an example module would be:

DBusFrameworkModule glib_framework = {
 "GLib",
 dbus_g_framework_init,
 dbus_g_framework_shutdown,
 dbus_g_framework_setup_connection,
 dbus_g_framework_setup_server,
 dbus_g_framework_idle_add,
 dbus_g_framework_idle_remove
};

Then, if a "framework module" is installed, libdbus uses it when it
creates a new server or connection, etc.

A base "GLib binding" would just be a single function, dbus_g_init(),
which would install the framework module. In this case, you would use
the basic C API. A higher-level "GLib binding" would also add an
object mapping to GObject.

Regarding a wrapper: my thought was to make it _not_ a wrapper, i.e.
dbus.h is included, unlike with dbus-glib which does not include
dbus.h in its headers.

> - thread safety
>
> We probably need a mutex to guard access to helper. Should all
> GBusName and GBusNameOwner be refcounted? I'm thinking a thread is
> running a filter registered for name tracking, while user calls from
> another thread to unregister the name tracker.

While thread locks on global data structures are good, I don't think
this API needs to do anything else with threads. The name tracker's
thread will just be the thread that the main loop does dispatch()
from. This depends on the main loop, and if we're main loop agnostic
there's nothing we can do about it.

> gbus_register_connection_tracker() and
> gbus_unregister_connection_tracker() at all?

This is one of the things I think has to be less convenient without a
main loop. However, you could implement them by having the
DBusFrameworkModule struct above include an "add idle handler"
function, so that might be a nice solution that would allow
maintaining the current API.

What I intended here is that an app could have *only* declarative code
(i.e. static information, like the desired bus name) and then
callbacks. The app ideally never has procedural stuff like
dbus_bus_get, instead the callbacks just get the connection when the
bus name is owned.

So the app says:
 "when I get name XYZ, call my callback"
and the library does:
 "connect to bus, request name, when name is owned call the callback"

This even allows support for restarting the bus, as some people have wanted.

> What is the definition of single instanace? My interpretation is "my
> application is the primary owner of the specific name". If this is
> correct then the mapping between GBusNameOwnershipStyle and owner
> flags would be like:
>
> GBUS_NAME_SINGLE_INSTANCE_REPLACING_CURRENT_OWNER =
> DBUS_NAME_FLAG_REPLACE_EXISTING
> GBUS_NAME_SINGLE_INSTANCE = 0
> GBUS_NAME_OWNED_OPTIONALLY =
> DBUS_NAME_FLAG_DO_NOT_QUEUE |
> DBUS_NAME_FLAG_ALLOW_REPLACEMENT
> Is it correct?

SINGLE_INSTANCE_REPLACING_CURRENT_OWNER would be ALLOW_REPLACEMENT |
REPLACE_EXISTING | DO_NOT_QUEUE

SINGLE_INSTANCE would be ALLOW_REPLACEMENT | DO_NOT_QUEUE

OWNED_OPTIONALLY would be ALLOW_REPLACEMENT

SINGLE_INSTANCE means the app wants one app at a time to own the name,
but the app can conventionally be started with a --replace command
line option. If it is, the app would specify REPLACING_CURRENT_OWNER
and take over as the single owner of the bus name. The *old* owner,
when it loses the bus name, would then get a callback (the same
callback it would get if it did not get the name in the first place).

OWNED_OPTIONALLY means we don't really care whether we have the name
or not, but we offer it and if nobody else owns it, we will.

> When a request is made on a name there could be
> error or one of the following results:
> DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER

> One of the questions is, if the result is ALREADY_OWNER, shall we
> still call owned_handler?

I would say call owned_handler when the state we know about in the
client side library goes from "not owned" to "owned," so if we were
already the owner, but in the given name owner object we did not know
that, we still call the owned_handler

> Also in what condition shall we call not_owned_handler? When failed to
> acquired a name, or ONLY when NameLost is received?

On failure also. The guarantee should be that at least one of the two
callbacks will be called at least once.

That is, you can write your app so that it "starts up" and does stuff
when it owns the name, and it exits when it loses the name.

Think of the state machine as { UNKNOWN, OWNED, NOT_OWNED } where when
you create a new NameOwner object it is UNKNOWN. Once the state leaves
UNKNOWN, it can never become UNKNOWN again. Anytime the state changes
to OWNED or NOT_OWNED, call a callback. If the connection disconnects,
go to NOT_OWNED.

> We can probably simplify things a bit by specifying not_owned_handler
> is to handle NameLost, while all the other cases are handled by
> owned_handler, which will require owned_handler be able to take more
> parameters like the return code and the error message.

I would not do this; you want to do the same thing in NameLost and on
initial failure to get the name. The point is, if you don't own the
name you would exit or stop providing services related to that name.
If you do own the name, you would keep running to provide services
related to that name.

I don't think it matters what the error or return code from the
request_name was; I would not provide them.

> Another way is to add another handler: failed_to_own_handler which
> handles error conditions and if return code being one of
> IN_QUEUE/EXITS/ALREADY_OWNER. And owned_handler is exclusively for
> PRIMARY_OWNER, and not_owned_handler for NameLost (probably we shall
> name it name_lost_handler.

Remember, this is a convenience API. If people really care about all
this they can do things by hand. But I don't think they ever care.

> gbus_unregister_name_owner (DBusConnection          *connection,
>                           const char              *owned_name,
>                          const GBusNameOwner     *owner,
>                           void                    *data)
>
> The question is why owner and data info is supplied again? Is there
> consideration that owner/data might be different from the call to
> register the name owner?

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.

Anyway, a final thought - again I think the app author should have to
type only two things, ideally: the declarative information such as the
name they want to own and the "style" of owning it; and the actual
application logic in callbacks.

We should avoid the app author ever having to call methods in order to
set up declarative state.

So in the name owner case, "here is the name I want, call
owned_callback whenever I have it, and call not_owned_callback
whenever I no longer have it, and call at least one of them right
away"

In the signal case, "here is the signal I care about, call my callback
if it ever happens"

For an async method call, "here is the outgoing method call message,
call my callback when it gets a reply or error"

And so forth.

Havoc


More information about the dbus mailing list