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

Havoc Pennington hp at redhat.com
Thu Aug 2 11:28:16 PDT 2007


Hi,

Thanks for the reply,

Thiago Macieira wrote:
>
> Automatic thread initialisation is one thing. Don't make me call 
> dbus_threads_init/dbus_threads_init_default because that breaks the state 
> the mutexes and wait-conditions keep. That simple thing will allow QtDBus 
> and another binding to work on the same process at the same time.
> 

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?

> Add the DBUS_VERSION macro so that I can update the code I create without 
> tricky configure-time checks.

This is simple, of course, and I already said it was OK.

> 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.

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?

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*

  - 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

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.

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.

However, for the "setup with GLib" there's already a "first one wins" 
flag (the function is a no-op on second and later calls). And if we get 
a "setup with GLib main loop" then a "setup with some other main loop," 
things are already going to fail, right... because the plugin has to 
know the main loop for reasons other than just dbus in all probability.

I'm happy to see a dbus_connection_get_is_dispatched() / 
set_is_dispatched() kind of flag, if it would help.

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.

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?

Of course if you are creating your *own* thread for the purpose of using 
a different main loop from the main thread, you would want to use a 
private connection.

But if you're using the app's default thread, it seems to me that you 
have to already know how to dispatch a connection (it's a global 
app-wide decision) and you have to know this for both shared *and* for 
private connections, which both have to hook into the thread-global main 
loop.

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.

Anyway... what I'm stuck on here is that I think a thread-global 
decision on the main loop to use is inherent. libdbus is certainly a 
context where the interoperability challenges of that global decision 
arise, but I don't think libdbus is the *cause* of the problem. The 
cause is that there can only be one poll() call at a time, per thread.

One way I see libdbus could be more flexible is that shared connections 
are app-global not thread-global. This is essentially saying "only the 
main thread and its main loop can dispatch shared connections" - the 
shared connections are owned/dispatched by the main loop used in the 
main thread.

But, I don't think this limitation should prevent the use of shared 
connections in the main thread, and it seems to me that we're using 
shared connections successfully in the GNOME stack. (mostly the shared 
connection to the session bus, of course)

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.

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()?

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

Havoc


More information about the dbus mailing list