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

Havoc Pennington hp at redhat.com
Wed Aug 1 15:15:28 PDT 2007


Hi,

Fan Wu wrote:
> I'm wondering if it's possible to stuff most of the dirty works into a
> background thread, and wrap the thread with some APIs. Users of the
> API need to supply callbacks to handle various events (just like gbus,
> but go one step further). If the application has its own mainloop,
> they can supply its own notifier callback and data(the handle/fd of a
> pipe/event), or just save this step, and leave the job of notifying
> mainloop to other callbacks if they feel necessary. The basic idea is
> these APIs are the ONLY interface to DBUS client and DBUS will hide
> the internal handling of thread and socket. Users shall not feel torn
> when making the mainloop/threads decision.

I'm not sure I understand what you mean here.

The fundamental issue is this: the main loop is inherently a per-thread 
global thing, because you can only poll() or WaitForMultipleObjects() on 
one set of stuff per thread.

As a result all code running in the same thread must agree upon the list 
of stuff to be poll()'d and have a way to add/remove items from the poll()

And this "agree what to poll()" mechanism is the main loop.

There is no way around that. For a method call, you can either:

  1) just block on the dbus connection, which will prevent anything
     else in the same thread from running

     a) do this in the main thread, hanging the app
     b) do this in a dedicated per-method-call thread, not hanging the
        app

OR

  2) add the dbus connection descriptors to the list of stuff that the
     thread will poll(), i.e. add the connection to the main loop,
     which allows the thread to block on multiple things at once
     such as the GUI and also the dbus connection

libdbus already supports your choice of 1)a), 1)b), or 2).

1)a) requires no work from the app but locks up the app.

1)b) requires the app to be multithreaded and thread-aware.

2) requires the app to do some work to integrate DBusConnection into the 
application main loop.

So all three are a bit sucky for apps using raw libdbus.

With the Qt, GLib, and other higher-level bindings, they use method 2), 
but since the main loop is known the binding can just integrate 
DBusConnection into it with no work on the part of the app.

That's what I don't know how to do in just a plain C library.

> One requirement of achieving this is that the DBUS thread must be able
> to be waken up in select/poll sleep. Otherwise the thread has to do
> frequent polling which is not desirable for handheld devices.

There should probably be a way to interrupt a dbus_pending_call_block(), 
though it means having a per-pending-call pipe and is thus expensive, so 
perhaps we'd have a dbus_pending_call_set_interruptible() or something 
to add the pipe.

Alternatively or also, we could have a block_with_timeout().

If you're needing those in order to write the following single-threaded 
code though, I think you're just using the library wrong:

    while (app is running) {
        block_on_app_stuff_ignoring_dbus_stuff();
        block_on_dbus_stuff_ignoring_app_stuff();
    }

You should either use two different threads and put a pipe in the dbus 
thread that you add to the main thread's poll(), or set up the 
DBusConnection so you include its descriptors in your poll().

Or, save yourself a lot of pain and use an existing main loop library ;-)

> The major concern I have is, the callbacks have to be thread safe,
> which might be hard to do it right.

If you want a callback in a separate thread, dbus does support that already.

Havoc


More information about the dbus mailing list