Running dbus_connection_read_write_dispatch in a separate thread

Simon McVittie smcv at collabora.com
Thu Jun 18 16:26:41 UTC 2020


On Thu, 18 Jun 2020 at 08:18:50 -0700, Thiago Macieira wrote:
> On Wednesday, 17 June 2020 10:37:53 PDT Alexandru Sorodoc wrote:
> > I will do that eventually, but for now I just want something as minimal
> > as possible. I will just set a timeout to the read_write_dispatch call
> > until I get to do things properly.
> 
> Or don't use read_write_dispatch. Poll the file descriptor yourself with a 
> poll() call that can be interrupted, then use dbus_connection_dispatch().

Expanding on that: read_write_dispatch is a simple solution for
single-threaded D-Bus clients like dbus-monitor, and is not really
fit-for-purpose in something with multiple threads.

Using a real main-loop framework (GLib's main loop, Qt's main loop,
libevent, libev, that sort of thing) is likely to be better-tested than
making your own artisanal dispatcher. Unfortunately, libdbus tries to
be all things to all people, so it half-supports all main loops and no
main loop equally, rather than fully supporting one and rejecting all
the others.

The libdbus API documentation has some wise words about libdbus which
probably apply here:

    This manual documents the *low-level* D-Bus C API. **If you use this
    low-level API directly, you're signing up for some pain.**

I think the key thing for interrupting the main loop is likely to be
dbus_connection_set_wakeup_main_function(), which needs to be called
providing a callback that will wake up the dispatching thread's poll()
(or equivalent).

For example,
https://gitlab.freedesktop.org/dbus/dbus-glib/-/tree/dbus-gmain
is a fairly complete example of integrating libdbus
with the GLib main loop. The callback that it passes to
dbus_connection_set_wakeup_main_function() uses g_main_context_wakeup()
to wake up the central poll() call in GLib. Historically
g_main_context_wakeup() was implemented as a pipe-to-self, and recent
versions optimize that by using an eventfd on Linux.

If "pipe-to-self" means nothing to you, then implementing your own event
loop is probably going to require more learning than would be productive
or efficient.

You could also consider using a different D-Bus implementation instead
of libdbus. GDBus (part of GLib) is specifically designed to be able
to share a single D-Bus connection between multiple threads, by using
GLib's GMainContext abstraction for threading and dispatching, and doing
the actual I/O in its own private thread.

I would normally also suggest systemd's sd-bus, but sd-bus is specifically
not multi-threaded, so probably not that one for your use-case.

Having all your D-Bus I/O happen in one designated thread, and making
all other threads that want to use D-Bus post messages to/from the D-Bus
thread instead of touching D-Bus APIs directly, is another possible route;
libdbus and sd-bus work well with that approach. This is close to how
GDBus works behind the scenes.

Regards,
    smcv


More information about the dbus mailing list