Watches and main loops
Marcin ‘Qrczak’ Kowalczyk
qrczak at knm.org.pl
Wed Jan 9 18:05:38 PST 2008
Watches
-------
dbus_watch_get_flags() is documented to return a subset of
DBUS_WATCH_READABLE and DBUS_WATCH_WRITABLE. Is it true that it always
contains either DBUS_WATCH_READABLE or DBUS_WATCH_WRITABLE, but not
both? It seems to be true in practice, and there is a rationale (either
direction can be toggled independently, assuming that a watch never
changes its flags once created, and I am making use of this assumption).
If it was always true and documented as such, then I could make a minor
simplification in my binding code. Actually I already make a minor
violation: if dbus_watch_get_flags() lists both directions (which I
suppose is impossible) and fd is ready in both directions, then I make
two dbus_watch_handle() calls, each reporting only one direction.
In which cases DBUS_WATCH_HANGUP or DBUS_WATCH_ERROR can occur? I know
this is not really a dbus question, but perhaps you will know if it is
safe to just return readable or writable (according to flags asked to
monitor) when poll/epoll says POLLHUP or POLLERR. Then dbus doing the
actual read() or write() would discover that something is wrong with the
socket, right? In my case it would be a bit inconvenient to pass that
information from poll/epoll to dbus.
I made a perhaps unusual main loop integration of dbus with the runtime
of my language Kogut. A watch is associated with a Kogut thread which
does blocking I/O (actually with 0 to 2 threads depending on the
direction), but a single OS thread can run multiple Kogut threads, and
blocking I/O is implemented in terms of a central select/poll/epoll
loop. Timeouts have their threads too. Only one OS thread at a time
calls dbus functions on a given connection, and I don't use those dbus
functions which block without entering the main loop.
Dispatching messages
--------------------
Everything works in my experiments, but I'm not sure where to call
dbus_connection_dispatch(). Currently I am creating another Kogut thread
per dbus connection, which runs a loop:
- wait for a Kogut condition variable until
dbus_connection_get_dispatch_status returns DBUS_DISPATCH_DATA_REMAINS
(the condition variable is notified by the function registered with
dbus_connection_set_dispatch_status_function receiving
DBUS_DISPATCH_DATA_REMAINS)
- call dbus_connection_dispatch()
(modulo DBUS_DISPATCH_NEED_MEMORY which is not handled well enough yet:
the dispatching thread exits but threads using the connection do not
notice this).
This design means that message filters and functions registered for
object paths are called asynchronously by some non-main thread, so they
must synchronize with one another and with the rest of the program if
they modify shared data.
I don't know if this can be designed better. The low level actions are
physically serialized, then Kogut threads are built upon them, but
then I suppose that in some potential uses of the binding the actions
of the threads are serialized again on the high level. I don't have
experience with using dbus or even with the event-driven style, so I
can't judge what is typical.
(Note that I don't expect anyone to use this binding in practice in
foreseeable future, I'm just playing with a library which is interesting
to wrap in my language. Maybe at least I will use it some time.)
If the dispatching thread is not created automatically (I currently
leave a choice), then the application would have to call one iteration
of the above loop concurrently with other events. While I do have an
experimental design of an event queue which serializes events reported
by concurrently running blocking functions, this looks weird. Since the
message is not returned by dbus_connection_dispatch(), but passed to
registered callbacks, then the only thing gained would be automatic
serialization of these callbacks wrt. reporting of other events, at the
cost of increased complexity of having to remember to dispatch messages,
and of actually creating an event queue which otherwise might not have
to be necessary at all. I can't judge whether this would be more
convenient. Perhaps I'm not explaining things clearly enough because
the design space is not clear for me. Help!
A drawback of having the dispatching thread is that: in case the
application doesn't register any objects or method filters but only
calls methods of remote objects, the dispatching thread is not necessary
in times when there are no pending calls, but it runs all the time.
By running all the time, it prevents garbage collecting of the whole
connection. I don't like objects which live forever when dropped.
Note that the whole design allows for another leak: if a registered
object or message filter refers to the initial connection object,
ignoring the fact that it gets it as a parameter, then the whole
structure is not garbage collected even if it does not have a
dispatching thread and is thus unreachable and invisible (it contains
a cycle which spans the garbage collected world and the C world).
I don't see a way to prevent this leak. Of course a sensibly written
application should not create such cycle but would use the connection
object passed as the parameter of the callback.
--
__("< Marcin Kowalczyk
\__/ qrczak at knm.org.pl
^^ http://qrnik.knm.org.pl/~qrczak/
More information about the dbus
mailing list