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

Fan Wu wufan9418 at gmail.com
Wed Aug 1 16:26:32 PDT 2007


HI Havoc,

Thanks for the comment!

What I'm thinking it's acutally a variant of your 1)b. The only
difference is the threading part is hidden in the API. So pseudo code
would be like this:

//this will spawn a worker thread in the background
// which will try to connect to the bus,
// try to hold a name etc
dbus_convenience_api_initialize(bus_name,
                                callbacks,
                                name_to_own...);

//in the main thread
while(continue_looping) // or poll/select/WaitforMultipleObjects
{
   ...
   if(got_stuff_to_send)
   {
      //this is the part that needs to wake up worker thread
      //in case it's in poll/select sleep
      dbus_convenience_api_send(conn, msg);
   }
   ...
}

//wake up the worker thread and exit it
dbus_convenience_api_finalize(handle)


As you can see it's not something utterly different/new, just a little
bit "convenient".

In this model, the responses shall be delivered through callbacks and
user shall not use dbus_pending_call_block(). The particular sleep I
want to wake up the thread from is that of select/poll. Poll with
timeout is not a good way out since polling too often the batteries
(of small devices) are drained, while polling too sparsely renders the
system not responsive.

The solution I'm thinking about is, add a wakeup handle (unamed
pipe/socketpair for Unix, unamed event for windows) to the transport,
then in socket_do_iteration() both the data socket and the wakeup
handle get polled. If the data socket is blocking, the sleep can be
interrupted through the wakeup handle. The next question is the timing
of using the wakeup handle. The socket is programmed to block
infinitely (unless it's waiting for a reply), so if there is only one
sending thread, the sender shall be able to use the wakeup trick
whenever it fails to acquire the IO path. If there are multiple sender
threads (it's possible but not recommended by the model above), it
shall not be a problem by wakeup worker thread multiple times. The
worst case is the worker wakes up and find outgoing queue is empty. In
this case it shall go back to blocking again.

This wakeup feature is intended for client only.

Thoughts?

Thanks,
Fan


On 8/1/07, Havoc Pennington <hp at redhat.com> wrote:
> 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