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