C convenience lib (was Re: Is dbus_pending_call_set_notify() thread safe?)

Thiago Macieira thiago at kde.org
Thu Aug 2 13:20:53 PDT 2007


Havoc Pennington wrote:
>This one I don't understand... Only the first threads init does
>anything; the rest are no-ops. Also, we changed it so threads can be
>initialized "late" after libdbus has already been used. So if everyone
>who needs threads inits threads (ideally with init_default), then stuff
>should work fine.
>
>What breaks in that case?
>
>I agree it might be somewhat more convenient if we called init_default()
>when creating the first mutex if threads have not been initialized at
>that point. But I don't understand what's _broken_ or _impossible to do_
>right now. What am I missing?

Nothing. I've just realised that there's a check to make sure it's no-op. 
I hadn't seen it before, which made me think that calling it more than 
once was dangerous.

>> Right now, QtDBus will not handle the shared DBusConnection-s. If a
>> library or a plugin wants to use those, it must make sure something is
>> handling the mainloop integration. I have no solution for this one.
>> And I don't plan to make QtDBus handle them anyways. So I'd recommend
>> moving to *deprecating* the shared connections completely.
>
>There are maybe a few separate issues here, I'll think out loud.
>
>1) First, which main loop is a particular thread using? That always has
>to be known and decided in some way. Shared vs. private connections
>don't matter, because *all* connections in a given thread must use the
>same main loop. Creating a private connection does not help; if the
>thread is using the GLib main loop, and I create a private connection,
>then I *must* set up the private connection with the GLib main loop or
>it won't get dispatched. So the "which main loop" decision is
>thread-global for *all* connections in the thread, right? Having the
>connection shared or not doesn't matter for that.

[summary at the end if you want to reply to it instead]

You're talking about "connections in the thread". That's not a concept in 
libdbus.

But I get your meaning and you're right: if some code wants to set up a 
*private* DBusConnection, it must know how to integrate with the mainloop 
that is handling that thread.

>In other words, I can't use the Qt bindings in my plugin if the main app
>uses GLib, regardless of whether the connection is shared or private,
>unless I create a separate thread.
>
>Am I right that the main loop decision is *inherently* thread-global,
>regardless of what libdbus does?

True. It is.

>2) Second, how should a library or plugin set up a connection's main
>loop (take care of dispatch on the connection)? Here are the approaches
>I can think of:
>
>  - only work with an app that uses a certain main loop, for example
>    a plugin for GEdit can assume GLib main loop. This means the
>    plugin could either just assume the main session bus connection
>    is already set up to dispatch, or the plugin could call the
>    setup_with_g_main() thing on all connections (which sets up only if
>    it hasn't already been done)
>
>    This is incidentally how Xlib works. If GDK sets up the Xlib event
>    queue, you can't run Qt on the same Display*

Yep. That's the solution so far.

>  - start a separate thread to do the dbus stuff, which means the
>    plugin or lib can use the main loop of its choice in that thread,
>    though it probably has to still hook a pipe into the app's main loop

Note: private connection.

>What I would ask here is, are there any changes to libdbus that would
>enable approaches other than these two? I can't really think of any,
>because the main loop is inherently thread-global, so the plugin either
>needs its own thread, or it needs to know the main loop for the main
> thread.

No, not that I can think of. That's why I said I don't know a solution for 
this problem.

>We could move the "this connection has dispatch taken care of" flag to
>DBusConnection, which would give us a "first one wins" situation like
>threads_init for setting up the dispatch.

I don't think that's enough. What does a plugin do if it tries to use the 
connection and dispatch isn't taken care of? If that plugin is meant to 
be mainloop-agnostic, it can't register a mainloop because it doesn't 
know one.

>3) Third, do shared connections work? The purpose of shared connections
>is to avoid extra overhead and file descriptors.
>
>As far as I know, if I have a few different libraries and plugins *in a
>thread that has decided on a main loop, so all the plugins and libs use
>the same one* then there is no problem with all those libraries and
>plugins using the same shared connection.

Right. But that's probably a private connection that was shared using a 
binding API. Those aren't the libdbus-1 shared connections.

>So for example, in a GLib app with GLib-using plugins and libs, I don't
>see why the app and the plugins/libs can't all share a connection. In
>fact, I think we are doing that quite a bit in GNOME and it successfully
>saves overhead and file descriptors.
>
>What am I missing?

Sharing *A* connection is ok. Using *THE* shared connection (dbus_bus_get, 
dbus_connection_open VS dbus_bus_get_private, 
dbus_connection_open_private) isn't.

Even if libdbus-glib-1 is sharing the dbus_bus_get connection, it has made 
it a private connection because no one else can use it. That's how QtDBus 
does it: the main connection to the system bus is shared among all users 
of QtDBus, but that's a dbus_bus_get_private connection.

Hence my request to deprecate the connections shared at libdbus-1 level. 
If you know your mainloop and if you know that a given connection has 
been set up, you have access to some higher-level API (the binding) that 
will give you the connection you need. That is, a plugin developer write:

	DBusGConnection *conn = dbus_g_bus_get(DBUS_BUS_SESSION, &error);
or:
	QDBusConnection conn = QDBusConnection::sessionBus();

instead of:
	DBusConnection *conn = dbus_bus_get(DBUS_BUS_SESSION, &error);

>Here is something that might clarify: the shared connections are
>intended to be *shared by all apps and plugins using the app-global main
>loop* - i.e. it is *expected* that if the app-global main loop is GLib,
>then the shared connections are "owned" (dispatched) by GLib.

Which confirms my thesis: the shared connections are, in fact, private. So 
there's no distinction between them.

Therefore, to avoid silly errors of applications using the connections 
shared by libdbus-1, deprecate those functions. Leave only the private 
connections, for which you *know* you have to set up the integration with 
the mainloop.

>Another thing we could do is allow setting a "setup new shared
>connection" callback, that would be passed all newly-created shared
>connections. At whatever point in the code an app chooses the main loop
>for its main thread, the app could set this callback and thus "own"
>setup of dispatch for all shared connections.

Which is what I don't intend to provide in QtDBus, though I did think 
about it. Too much work to integrate properly with the Qt event loop, 
keep states, etc., for too little gain. And I don't think it would solve 
all problems, such as when a mainloop-agnostic plugin got loaded in an 
application that isn't D-Bus-aware.

>Are you avoiding shared connections just due to main loop issues? If so,
>isn't it true that a library has to use the Qt main loop anyway, even
>with a *private* connection, since the app is going to be blocking in
>the Qt poll(), not the library's desired poll()?

No, there are more issues. QtDBus makes a lot of assumptions about the 
state of the connection. For instance, it assumes that all messages it 
receives are supposed to be handled by it, which is only true if no one 
else is sending messages or adding match rules behind its back.

But you're right about the poll/select: you shouldn't block on another 
mainloop's poll/select inside a QEventDispatcher-managed thread.

>Or are there other shared connection bugs we need to fix aside from this
>main loop stuff?

Let me summarise what I think:

We can divide D-Bus-using code in two categories: mainloop-specific or 
mainloop-agnostic (for whatever reason).

We can also divide it in two other categories: using a binding or using 
libdbus-1/wrappers.

I am arguing that those two divisions should be one and the same: the very 
definition of "binding" is to bind libdbus-1 to a mainloop framework.

So, if you're writing a mainloop-specific code, you must assure that the 
binding is loaded into memory and is working. If you know that is the 
case, then why not use the binding already?

Therefore, I argue that the only valid reason for using libdbus-1/wrappers 
directly, instead of a binding, is to write mainloop-agnostic code. And 
since you must not assume anything about mainloops, you must handle your 
connection's dispatching yourself. That means dbus_bus_get_private and a 
private thread for handling asynchronous events.

-- 
  Thiago Macieira  -  thiago (AT) macieira.info - thiago (AT) kde.org
    PGP/GPG: 0x6EF45358; fingerprint:
    E067 918B B660 DBD1 105C  966C 33F5 F005 6EF4 5358
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 189 bytes
Desc: This is a digitally signed message part.
Url : http://lists.freedesktop.org/archives/dbus/attachments/20070802/7e718daf/attachment.pgp 


More information about the dbus mailing list