DBus threading question

Schmottlach, Glenn glenn.schmottlach at harman.com
Thu Dec 11 10:01:25 PST 2008



Please forgive me, but I thought I would cross-post from the DBus C++
mailing list this since there's a chance that readers here might have
experience with the DBus C++ binding. Any insights would be helpful . .
.

--------------------------------------------------------

I have a few questions regarding the recommend pattern to create a
client application that makes several calls to a DBus service while
subscribed to signals from that service (or others). In particular I'm
interested in being aware of any threading caveats which might cause
dead-lock conditions or abnormal/strange behavior. 

Let me describe what I think a typical client application might look
like: 

static DBus::BusDispatcher dispatcher;
static volatile bool gExit(false); 

void sigHandler(int sig)
{
   dispatcher.leave();
   gExit = true;
}
 

void* workerThread(void* arg)

{
   DBus::Connection* conn = reinterpret_cast<DBus::Connection*>(arg);
   try
   {   
      MediaServiceClient client(*conn, MEDIA_SERVICE_OBJ_PATH,
                                 MEDIA_SERVICE_BUS_NAME);
      while ( !gExit )
      {
         client.PlayAction(NMediaService::PLAY_ACTION_PLAY);
         sleep(1);
         client.PlayAction(NMediaService::PLAY_ACTION_PAUSE);
      }
}
 

int main(int argc, char* argv[])
{
   pthread_attr_t attr;
   pthread_t worker;
   bool threadStarted(false);

   signal(SIGTERM, sigHandler);
   signal(SIGINT, sigHandler);

   DBus::_init_threading();
   DBus::default_dispatcher = &dispatcher;
   DBus::Connection conn = DBus::Connection::SessionBus();
   pthread_attr_init(&attr); 

   if ( EOK != pthread_create(&worker, &attr, workerThread, &conn) )
   {
      cerr << "Failed to create thread" << endl;
   }
   else
   {
      threadStarted = true;
      dispatcher.enter();
   }

   cout << "Terminating client . . ." << endl;

   if ( threadStarted )
   {
      pthread_join(worker, NULL);
   }     

   return EXIT_SUCCESS;
}

The service for which the client (MediaServiceClient) is generated also
emits signals which the client receives. The handling of these signals
is implemented by providing an implementation for the purely virtual
callback methods in the generated client proxy. These implementations
are found in the MediaServiceClient (which inherits from the generated
client proxy).

Here's where the questions related to concurrency and thread-safety
enter the picture. The worker thread loops (until the gQuit flag is set)
and continuously makes synchronous (blocking) calls to toggle the play
state of the media player. Based on my (limited) understanding of the
logic, a synchronous method call causes the thread to poll/select on a
socket descriptor waiting for a specific reply. If any signals (or
unrecognized replies) arrive during this process they're placed on a
queue to be process later. So this seems to imply that when the
synchronous call is made from the worker thread, the "main" thread that
called dispatcher.enter() is also polling/selecting on the same socket
descriptor at the same time. This means that when a message does arrive
both threads (the "main" and worker thread) are each vying to dispatch
the message. Is this the correct interpretation of what is going on
internally?  How is the message handling arbitrated? What happens if the
"main" thread retrieves the expected reply message that the worker
thread is waiting on? 

Things are also a little murky when considering signal handling. In my
example above, it appears that that the signal callback methods should
be executed on the "main" thread (from inside the dispatcher.enter()
function). This means the signals arrive on a thread different than the
worker thread that is making requests to the service. In general, is it
safe to call methods on the service from these signal handler callbacks?
My suspicion is that it's not a wise thing to do. If service methods
need to be called as a result of receiving a signal, should the signal
handling callbacks do nothing more than bundle the information into a
context that is somehow delivered to the worker thread for processing?

Can someone offer any suggestions on the "correct" way to implement a
client like this? The C++ binding examples don't appear to address any
of these issues and the non-existent documentation doesn't help ;-)



More information about the dbus mailing list