[Phone Server dbus API]: sync vs. async

Marcel Holtmann marcel at holtmann.org
Wed Jan 30 18:29:19 CET 2008


Hi Mickey,

> as you might know I'm working on the dbus API for the forthcoming phone 
> server. There's a conceptual disagreement about whether to use a mainly 
> synchronous or mainly asynchronous API, e.g. would you rather call a method 
> that returns the results or call a method that triggers a signal that will 
> eventually come.
> 
> The synchronous API may be simpler to follow, however the asynchronous API is 
> more natural since both the modem and the UI are basically event-based 
> models.
> 
> However, it looks like 90% of the existing dbus APIs are using the synchronous 
> approach.
> 
> This decision is quite important as it lays out the base for the phone server 
> architecture and possibly eventually a reimplementation of the gsm 0707 
> backend.

the first thing that you have to understand is that D-Bus in itself is
fully asynchron. The illusion that you have the synchronous calls comes
from the fact that methods can behave like function, but that is not how
D-Bus actually works. A method call is a D-Bus message. Nothing more and
nothing less. A method message can be answered by a reply message or an
error message or by now message at all. This return message can come at
any time. However in case you use synchronous/blocking functions from
the D-Bus API this will look like a synchronous approach, but it isn't.
What you are doing is that have the mainloop waiting for the response.
This is feasible for command line utilities or in cases where the order
of method calls are required, but in most cases you wanna use
asynchronous method calls. Especially within UI applications, because
otherwise you might block the mainloop of the application. It is really
important to understand this difference. Using D-Bus in a synchronous is
wrong most of the times.

The D-Bus message calls also imply the concept of timeouts. In the case
a method is not marked/annotated as no-reply, it has to be answered with
an error or a reply. Otherwise the the system/session bus will generate
a timeout event and return with an error. The timeout of a message call
is flexible and the caller can set this timeout. In most cases the
default value works perfectly fine, but in some other cases it might be
a good idea to extend the timeout. Mainly when you deal with hardware
and know it can take some extra time before you can generate the reply
message.

Also keep in mind that during the D-Bus message callbacks you are
blocking the mainloop. Compare it to code that is handled inside an
interrupt. You wanna do this as quick as possible. Otherwise your intend
is to use it asynchron, but the server/service makes it synchron.

That said, signals are also only messages. The only exception is that
they don't imply a return message and that they are broadcasted.

Now to your question. Using D-Bus method calls is perfectly fine and the
right approach to do many things. However looking from the exporting
perspective is always wrong. Look from the user perspective and define
the API that a user aka UI application (or multiple) really needs.

This means that any kind of polling is wrong. No matter what, because
waking up for status changed detection is only draining the battery. So
if any state or value etc. changes that the UI needs to be informed of,
send a signal. This allows application to listen to events without even
having to deal with the whole picture. For example if you have a nice
applet that only shows the network name, it will on startup us a method
call (GetNetwork for example) to get the initial network name and
display it. After that it will only listen to a signal (NetworkChanged
for example) to update its display. I prefer the idea of totally stupid
UIs whenever possible. Move the hard work into the daemons.

In conjunction with this example you might have a method to change the
network (SetNetwork for example). This will be called from a total
different application (network settings) and you only have to make sure
that in the end it emits a signal when the network change has taken
place so the stupid network name display UI can update its information.

In some cases you wanna use method that might take longer and involve
multiple values to return. In this case you have a method that triggers
a certain action and all results are returned as signals. This is a good
idea for starting a scan operation or something that really takes a long
time. There are multiple ways to get this kind of task done. My advise
is too keep in mind if the reported information can be used by other
applications at the same time. So some times you have to indicate that
an operation started and when it ends.

Besides this discussion it is important to design the API in a way it is
friendly for the UI and even more important efficient. Round-trips to
the D-Bus should be avoided whenever possible. The best way to do this
is to use dictionaries as return values. Don't use structs. They are too
inflexible and painful to handle. The dictionaries can be easily mapped
to native types of the binding, while for the structs you have to deal
with complex marshaling or iterate of the message parameters. Both is
painful and makes code really complex and hard to understand.

Also the use of integer constants is a bad idea. Simply use string
constants. For example for the network status use "unknown", "available"
and so on. The use of integer constants leaves room for error and doing
it from higher languages like Python or Perl is painful. Using strings
is much more easier there. Not to mention that debugging becomes really
easy if you have plain strings.

Another small tip from me that you might not wanna make the API stable
and frozen before you actually have written the UI. When you start
writing the UI or any kind of application using that API you see how
good it really is. A lot things look good when you design it, but when
you start using it, it becomes more and more problematic. Make it easy
for the UI should be the first rule of business.

Don't look too much at other D-Bus APIs. Especially the ones that are
around for some time. To be quite honest, most of the early adopters had
no idea what they were doing at that time. And I am happily include me
to that list. We learned from our mistakes and have now a solid ground
for D-Bus API design, but we still live with mistakes we made. Once you
understand the full potential of D-Bus and how to use it properly it is
powerful. If you try to apply stupid C/C++/Python/OO etc. API to it you
make your life unnecessary hard.

With D-Bus you can play a lot of nice tricks that allows you the best
separation between UI and daemon code. One example might be the agent
concept to allow proper callbacks into UI code.

If you want me to, I can have a full review of your current API. I only
just had a quick look, but there are a lot of things that can be
simplified.

Regards

Marcel





More information about the openmoko-devel mailing list