Thursday, June 7, 2007

Functor based multithread callbacks in C++

One of the most powerful constructs in C++ is function pointers as inherited from C language, C++ however provides an even more powerful and elegant construct in place of function pointers also known as the functor. Functors themselves deserve treatment on their own and I highly recommend the reader to glance over their use in STL and BOOST as powerful and sophisticated mechanism to provide encapsulation and reuse supporting decoupled interfaces. This kind of encapsulation empowers the creation and enforcement of object oriented frameworks and is specially interesting in the context of call back methods in object oriented frameworks. The power and elegance of functors however is sometimes eclipsed when we are dealing with libraries that expect C type function pointer as opposed to functors. From the libraries perspective this makes perfect sense for number of reasons including but not limited to flexibility offered, compatibility with C language and inability or undesirability to break legacy interfaces. In this article we will use pthreads as example of such a library and demonstrate how easy it is to write an object oriented call backs for similar C style libraries. The idea is quite simple, we will create a simple object oriented framework based on a wrapper class for phtreads with a functor used as the callback mechanism for pthread_create(). Users instantiate this utility class with a worker object as an initialization parameter. The worker object implements a known interface providing callback functionality in a concrete start() method which is in turn called by the operator() from base worker class. Parameters can be passed to this callback either as part of construction or using setters depending on needs or team standards. Listing 1.1 shows the interface and a simple implementation of this idea.
class worker
{
public:
   void operator () () { start(); };
   virtual void start() = 0;
};

class message_worker : public worker
{
   std::string msg;
public:
   message_worker(std::string p_msg) : msg(p_msg) {}
   void start() { std::cout << msg; }
}

Listing 1.1. A simplistic interface and implementation of our Runnable
Next we focus on the wrapper class for pthreads library. This wrapper accepts a worker object as argument to its constructor and uses it as a functor inside a static dispatcher we call go() in our implementation. The difference between using a static versus non-static dispatcher is that non-static members require a lot of glue and hacks in order to simulate thread-safe callbacks in object oriented frameworks. Their call semantics can be different depending on compiler and implementation and so on. In contrast, static members have standard calling conventions which are compatible with the callback semantics of pthreads (and other threading libraries). There is one minor issue with using static dispatcher though, specifically whenever a member function is called, the compiler tacks on the value of associated "this" pointer as the first argument on stack which allows the member function to access state information associated with its respective instance - this in our case is the correct worker instance which we need to access. It turns out, we can simulate this behavior of pushing "this" pointer as the first argument to a member function quite nicely. As with most libraries that support callback functions, pthreads library also supports passing of a single word sized argument on the stack of its callback function. This word sized argument is usually a pointer to a data structure or shared variable but in our case, we will use this capability to simulate member function invocation by sending "this" as the argument to our callback() function. By passing "this" as the argument and address of the static dispatcher as the function pointer argument to pthread_create(), we can simulate calling our static callback method as if it were invoked on the current instance of our pthread wrapper class, the one that happens to have an initialized worker object as one of its state variables. At this point all the callback method go() has to do is invoke the worker() using passed "this" pointer and that is all she wrote. Listing 1.2 shows a basic implementation of this pthreads wrapper class along with a simple use case putting all of this together.
class worker
{
public:
   void operator ()()
   {
       start();
   }
   ;
   virtual void start() = 0;
};

class message_worker : public worker
{
   std::string msg;
public:
   message_worker(std::string p_msg) :
       msg(p_msg)
   {
   }
   void start()
   {
       std::cout << msg;
   }
};

class thread
{
   worker& work;
   pthread_t id;
   pthread_attr_t attr;

public:

   thread(worker& p_work) : work(p_work)
   {
      pthread_attr_init(&attr);
   }

   void start()
   {
       pthread_create(&id, &attr, (void* (*)(void*))&go, this);
   }

   static void go(thread *working_class)
   {
      working_class->work();
   }

   int join()
   {
       return pthread_join(id, NULL);
   }

   ~thread()
   {
       pthread_attr_destroy(&attr);
   }
};

int main(int argc, char *argv[])
{
   message_worker m1("Hello");
   message_worker m2(" ");
   message_worker m3("world");

   thread t1(m1);
   thread t2(m2);
   thread t3(m3);

   t1.start();
   t2.start();
   t3.start();

   t1.join();
   t2.join();
   t3.join();

   return 0;
}
Listing 1.2. Complete listing of thread dispatcher including use case
As you can see the pthread wrapper class acts as thread safe utility to wrap platform specific API calls while allowing our runnable to be maintained portably and easily in an object oriented framework. Clearly there is room for improvement here in terms of performance and decoupling of Runnable framework from platform specific thread API wrappers completely. In one of our future articles we will address both of these areas using templates for enforcing the interface and compile time method dispatch to avoid vtable related performance hit, if that floats your boat.

No comments: