dbus-glib Roadmap
This is an area to collect various thoughts on the future of using dbus from glib/gobject code. Please feel free to add any thoughts at all here for now. We will try to generate a consensus!
Interoperability
These need sorting out first, really...
- Signature ('g') type not supported - simply supporting it as a boxed gchar * would be considerably better than nothing
- Can't have GPtrArrays (possibly also GArrays?) as the values in a GHashTable, so we can't represent e.g. a{saai}
- We can't listen for signals "globally" (compare dbus-python's dbus.connection.Connection.add_signal_receiver())
GObject introspection
One option that's been discussed a lot is to use gobject-introspection to allow a programer to directly attach an object to the bus.
Work needed:
- Add in/out/ownership to Gobject-introspection genintrospect.
- Write layer for mapping dbus messages to invocation on introspectable methods
- - This would take two sets of xml, the dbus intropspection xml to specify the expected wire format and the gobject-introspection xml of the interface to which to map the dbus method calls
- gobject signal to dbus signals (again using the gi and dbus xml to generate the marshallers to map gobject signals to dbus signals)
- Gobject Introspection to D-Bus introspection conversion tool.
- D-Bus introspection conversion to Gobject Introspection tool.
- Dbus gobject proxy generation from gobject-introspection
- - this would take two sets of xml, the gobject-introspection specifying the c interface to generate and the dbus introspection specifying the wire protocol to map to.
Hippo D-Bus helper - a lower-level binding
Other discussed possibilities is the extension of Havoc's hippo-dbus-helper.
RobTaylor: IMHO, both modes of directly marshalling messages and component object bindings should be useable by an applictation, different situations call for different approaches.
TpProxy from telepathy-glib >= 0.7.1
SimonMcVittie considers TpProxy and TpDBusDaemon in telepathy-glib darcs (note: UNRELEASED, API may change) to be a reasonable prototype for next-generation D-Bus bindings in GLib.
The naming is only for namespace reasons - they're designed to be generic (wrappers to avoid the deficiencies of the current) D-Bus GObject bindings, and could reasonably be renamed and moved into dbus-glib. They don't use any undocumented or internal API from dbus-glib, except for the format of the "glue" in dbus-binding-tool (which ought to be documented anyway).
How it works: TpProxy is a proxy for a remote object (unlike DBusGProxy, which is a proxy for one interface of a remote object).
There are two levels of API, proxy.h (for general client use) and proxy-subclass.h (for subclasses and mixins only - may change drastically as we improve dbus-glib).
For each interface, we generate (from augmented introspect XML) some "mixin" functions which use the proxy-subclass.h API; they take a TpProxy (or subclass) as first argument. For each method, there's a function to set up an async call, and a function to make a call that recurses the mainloop (much like gtk_dialog_run). We don't use libdbus' "blocking" methods that make method replies jump the queue, because the Grand Unified GInterfaces proposal (to be documented later) will probably not support those.
You can use TpProxy directly, but in telepathy-glib we subclass it (TpConnection, TpChannel, TpConnectionManager etc.) and mainly use the subclasses. The subclasses can have convenience functions implemented in terms of the generated code. TpDBusDaemon is a subclass of TpProxy to let you make calls to the bus daemon.
- D-Bus API comes from augmented introspection XML and is mangled into a GLib GInterface
- All user callbacks are called from high priority idle callbacks (g_idle_add_full) to ensure no locks are held (with some dbus-glib improvements, we could avoid this)
- We connect to signals using a connect_to_signal method, which could be extended with argument-matching later (and provides somewhere to hook in a call to the D-Bus daemon to add a match rule) - we never rely on signals "just getting emitted" like you can in GLib
the generated GInterfaces (for services) and TpProxy "mixin" methods (for clients) are 100% generated - you never edit them
- while we're generating code anyway, we generate typesafe static inline convenience functions, which take the Right(tm) parameters, for signal stuff and method returns (this has saved us from embarrassing bugs several times)
- there are regression tests for the core functionality (extremely important, some of the main loop interactions get rather subtle)
Grand Unified GInterfaces
SimonMcVittie and RobertMcQueen have vague plans for the generation-after-next D-Bus GLib bindings:
... better description to be written ...
- common GInterface on client and service side
- API designed around async calls, with a gtk_dialog_run-style convenience API (doesn't pretend to be normal local C code, because it isn't and we should admit that):
/* THIS IS JUST A SKETCH - precise API not even planned yet
*
* assume foo is the dbus-glib-ng namespace, bar is something
* like telepathy-glib that uses it, BarWidgetIface is a GInterface,
* and Bar widgets have a method GetBadgers(b: recursive) -> as
*/
/* the generated header would say something like:
* typedef void (*BarWidgetIfaceGetBadgersResultCb) (BarWidgetIface *self,
* const GError *error, const gchar * const *badger_list,
* gpointer user_data); */
static void
got_badgers_cb (BarWidgetIface *self,
const GError *error,
const gchar * const *badger_list,
gpointer user_data)
{
}
call = foo_call_new (proxy, 5000 /* timeout */);
bar_widget_iface_start_get_badgers (proxy, call, TRUE,
got_badgers_cb, some_user_data_or_other);
foo_call_run_until_completed (call);- ???
- Profit!
Ideally, the GInterfaces would be such that a service and a client in the same process could just call each other's callbacks directly.
This is partly why we don't want to spin in libdbus (e.g. dbus_connection_send_with_reply_and_block) - that fundamentally fails if the D-Bus part of the call is actually going to be optimized away.
Also, the deadlocks that result from blocking without re-entering the main loop have plagued e.g. Sugar Activity writers on the OLPC, which has convinced me that this mode of operation is fundamentally a bad idea.
Instead, we can recurse the main loop like gtk_dialog_run() does, to achieve a friendlier blocking call. We just need to remind authors that their code will be reentrant, e.g. with a _run_ method naming convention.
Design considerations
The world according to SMcV
Here are the things SimonMcVittie considers to be axiomatic:
- You shouldn't design D-Bus APIs via designing GLib APIs, that way lies madness (poor interoperability and mismatches between expectations and reality)
- Method calls must support async (callback) access
- Method calls should support blocking calls that re-enter the main loop
- Since originally writing this I've decided this one isn't such a great idea for any non-trivial code... D-Bus is asynchronous, client authors will just have to deal with it!
- User callbacks should be called without holding any locks or preventing any API from working
- If it's impossible to do this, the limitations must be documented clearly
- The API must still make sense when dbus-glib is able to do argument-matching (we shouldn't "paint ourselves into a corner" with inability to listen to subsets of signals because we're emulating a GLib object, like I fear Qt has done)
- every file in users' projects must either be source code or not, not some semi-generated mixture of the two
- telepathy-gabble used to generate stub GObjects on a fill-in-the-blanks basis - this led to extremely laborious merging when the D-Bus API changed
- GInterfaces are a good thing
- so is type-safety (let your compiler tell you when you haven't updated to the new API)
- if there's a varargs method, there must be an equivalent that can be called in a dynamic way by bindings and clever stuff (g_object_get has g_object_get_property; dbus_g_proxy_begin_call should have a version that replaces the varargs with a GValueArray)


