Having worked on XMMS2 for a long time I've seen many client developers come and go, and seen some really amazing clients being written. The one thing that we see again and again in this development process is developers not understanding the way xmmsclient works entirely and being confused as we over and over tell them "don't mix sync and async." So I'm going to explain why one shouldn't mix sync and async and what exactly that means.
XMMS2's client library (xmmsclient) implements an asynchronous binary IPC protocol (which I've talked about
in a previous blog post). Essentially being asynchronous means that clients are returned a deferred result
whenever they make a request to the XMMS2 daemon. We wrap this deferred result in an xmmsc_result_t object
(XMMSResult in the Python bindings). Upon making a request an xmmsc_result_t is initialized and registered
with the IPC result register by use of the xmmsc_ipc_result_register method. When the daemon has a response
it writes it to the clients IPC transport (tcp or unix sockets at this point). This data is read in by the
xmmsc_ipc_io_in_callback which calls xmmsc_ipc_exec_msg with the new IPC message. xmmsc_ipc_exec_msg
looks the corresponding result up in the IPC result register and calls xmmsc_result_run with the message
which is then parsed and made accessible by various xmmsc_result_get_ methods depending on the resulting
type. Most of this is pretty immediate except for the waiting for the response from the daemon and this is
where the asynchronous part comes in.
When data is received on the IPC transport xmmsc_ipc_io_in_callback needs to be called. To do this the
file descriptor of the IPC transport needs to be monitored and xmmsc_ipc_io_in_callback needs to be called
when data is ready to be read. The same goes for xmmsc_ipc_io_out_callback which needs to be called when
data is ready to be written on the file descriptor. There are a number of ways to monitor the file descriptor
which aren't all that important to understand. In an asynchronous setup an event loop is attached to the file
descriptor and calls xmmsc_ipc_io_in_callback when there is data to be read. Usually an event loop will make
provisions for monitoring arbitrary file descriptors because event loops should not be mixed since they can
confuse each other. Which rolls us right into why sync and async should not be mixed: synchronous IPC in
xmmsclient is done with a select loop.
When xmmsc_result_wait is called on an xmmsc_result_t a while loop is setup up which calls
xmmsc_ipc_wait_for_event until a parsed result is present in the xmmsc_result_t. Following over to
xmmsc_ipc_wait_for_event we run into:
[...]
if (select (fd + 1, &rfdset, &wfdset, NULL, &tmout) == SOCKET_ERROR) {
return;
}
if (FD_ISSET (fd, &rfdset)) {
if (!xmmsc_ipc_io_in_callback (ipc)) {
return;
}
}
[...]
So, xmmsc_ipc_wait_for_event sets up a mini-event loop to wait for data to read on the IPC transport which
is then handled by xmmsc_ipc_io_in_callback. What this ultimately means is that by mixing sync and async
the developer is mixing event loops, which can end up interacting poorly with each other and making a big mess
of things.
So basically, if you're developing an XMMS2 client you should either use an event loop or use xmmsc_result_wait
but you should NOT use both because they are not meant to play nice together and you can not be sure of what
they might do to each other.