Multithreading


Elegant Function Call Wrappers

Andreas Huber

Scheduling functions for later execution is an obvious requirement in multithreaded programs. How to do that and preserve both type safety and modularity is not so obvious. The author combines an old pattern and some new template techniques to pull it off rather nicely.


Introduction

Today’s software systems are increasingly being constructed employing many concurrent threads of execution. A thread has its own CPU registers and stack but shares other resources (e.g., heap memory, files, and so forth) with other threads in the same process. Cross-thread communication is frequently realized via asynchronous calls: instead of calling a function directly, the calling thread pushes a structure containing a function identifier along with the parameters into a queue and then continues execution. The other thread (called the worker thread) later pops the structure out of the queue and uses the information in it to call the function. The process of wrapping a function call for later execution is easy and straightforward. However, it can be a major source of code duplication in any program that makes asynchronous calls to a lot of different functions. In this article I present a mechanism that completely eliminates the duplication and makes asynchronous calls almost as easy to use as synchronous ones. This comes at the run-time cost of only one additional indirection per call.

This mechanism builds on a foundation laid by earlier work, so I’ll describe my solution in a bottom-up approach. I will compare the following options:

1) The C-style approach, which lacks type safety and tightly couples the worker thread to the calling thread;

2) The Command pattern [1], which makes function call wrapping type-safe and also decouples the worker from the caller but requires the programmer to implement a class for each function to be called asynchronously;

3) Option No. 2) enhanced with functor subclass templates, adapted from Andrei Alexandrescu’s “Generalized Functors” [2], which greatly reduce the amount of code necessary but require a somewhat difficult syntax; and

4) Option No. 3) enhanced with creator function templates to finally provide ease of use.

1) The C-style Approach

For projects using an object-oriented language this is not really a practical option anymore, but I give a quick overview so that the merits of the other options become more obvious: in C, the queued structure typically contains a number identifying the function accompanied by a void pointer referencing a second structure, a wrapper for the function arguments. Depending on the value of the number, the worker thread casts the void pointer to the right type and then calls the right function, passing the contents of the wrapper as arguments.

Obviously, there is a serious problem: in order to function correctly, the caller must push exactly what the worker expects. However, because of the casting involved, the compiler cannot help the programmer keep the two sides in sync. For example, if the number and the type of the wrapper pushed by the calling thread don’t match, the worker thread will likely crash. Moreover, the worker is tightly coupled to the caller so that both have to be modified whenever a new function is introduced. I show in the next section how the Command pattern solves both problems.

2) The Command Pattern

Ideally, the worker thread should not “know” anything about the calls it executes. The Command pattern enables this by providing an abstract base class that defines a minimal interface to execute calls. An example follows (with the name of the class changed to reflect common C++ usage):

class Functor
{
  public:
    virtual ~Functor() {}
    virtual void operator()() = 0;
};

Whenever you introduce a new function to be called asynchronously, you do the following:

1. Derive a new class from class Functor. The new class should have one data member for each function parameter.

2. Provide an additional data member in the derived class that stores a pointer to the object whose member function is to be called.

3. Define a constructor for the derived class that initializes (copy constructs) each data member with values passes as arguments to the constructor.

4. Override operator() to call the function, supplying the data member values as arguments.

For each function call, the caller then does the following:

1. Allocate a new object of the previously defined class.

2. Pass all the function arguments to the constructor.

3. Push a pointer to the object into a queue.

4. Resume normal operation.

The worker thread later pops the pointer to the object out of the queue, calls operator(), and deletes the object.

As mentioned above, the Command pattern solves the worst problems of the C-style approach. However, there is one problem with the C-style approach that the Command pattern does not solve either: code duplication. You have to derive a new class from Functor every time you want to introduce a new asynchronously called function. In a large program you may well end up with hundreds of these tiny classes. They all have the same semantics but differ in the names of the functions called, the types of the function parameters, and the number of parameters. As I show in the next section, C++ templates are perfectly suited to generalize these differences and to eliminate the need for hand-coding subclasses of Functor.

3) Functor Subclass Templates

Have a look at the following class template derived from Functor. (Yes, the code examples in this section look a bit ugly, but I will clean that up in the next section).

template< class CalleePtr, class
  MemFunPtr, class Parm1 >
class MemberFunctor1 : public Functor
{
  public:
    MemberFunctor1
    (
      const CalleePtr & pCallee,
      const MemFunPtr & pFunction,
      Parm1 aParm1
    ) :
      pCallee( pCallee ),
      pFunction( pFunction ),
      aParm1( aParm1 )
    {
    }

    virtual void operator()()
    {
      if ( ( pCallee != NULL ) && 
           ( pFunction != NULL ) )
      {
        ((*pCallee).*pFunction)(aParm1);
      }
    }

  private:
    CalleePtr pCallee;
    MemFunPtr pFunction;
    Parm1 aParm1;
};

Please note that in operator() I could also have written ( pCallee->*pFunction )( aParm1 ) to make the call through the function pointer, but then pCallee could not be a typical smart pointer, because most smart pointers do not define an operator->* [3].

MemberFunctor1 offers a universal solution for calls to member functions with one parameter. In the online code (www.cuj.com/code) you will find versions for zero to six parameters for all C++ function types, seven class templates for member functions, and seven more for free functions and static members.

The template could be used as follows:

#include <iostream>

class Foo
{
  public:
    void Bar( const int & rVal ) { std::cout << rVal; }
};

int main( int argc, char* argv[] )
{
  Foo aFoo;
  float aValue = 1.0;

  // really ugly!
  typedef MemberFunctor1
    < Foo *, void ( Foo::* )( const int & ), float >
    FooBarFunctor;
  Functor * pCall = 
      new FooBarFunctor( &aFoo, &Foo::Bar, aValue );

  ++aValue;

  // the following would be done by the worker thread
  // pop call out of queue
  ( *pCall )();

  return 0;
}

Now, this doesn’t look too promising, does it? Especially the second template parameter in the typedef, a pointer to a member function of Foo accepting a const int & and returning void, is a syntactic nightmare (see [4] for a complete discussion). I’ll clean that up in a minute. Because I am dealing with pointers to Functor, the worker thread has to call operator() with the somewhat unusual syntax ( *pCall )(). I could encapsulate the pointer into a handle object (see [2]), which then would behave exactly like an STL functor, but since this call is only made once within the worker thread, I decided to stick to the slightly more unhandy pointers.

4) Creator Function Templates

To facilitate the creation of objects from class templates, the STL supplies function templates like std::make_pair. The make_pair template takes two parameters of some types T and U, which it uses to construct an object of type std::pair< T, U >. It returns this object to its caller. The nice thing about make_pair is that you can call it without having to explicitly specify the types T and Umake_pair deduces these types from the arguments passed to it.

I provide the same sort of function template below. (I prefer the name DeferCall instead of something like MakeFunctor because it describes much better what really happens.)

template< class CalleePtr, class Callee, class Ret,
   class Type1, class Parm1 >
inline Functor * DeferCall
(
  const CalleePtr & pCallee,
  Ret ( Callee::*pFunction )( Type1 ),
  const Parm1 & rParm1
)
{
  return new
     MemberFunctor1< CalleePtr,
       Ret ( Callee::* )( Type1 ), Parm1 >
         ( pCallee, pFunction, rParm1 );
}

Using DeferCall, function templates don’t have to be explicitly specified (that is, you don’t have to fill in the template arguments corresponding to CalleePtr, Callee, Ret, Type1, and Parm1). You can call them like normal functions and the compiler automatically instantiates them for you:

// the following line replaces the new 
// expression AND the ugly typedef
Functor * pCall = 
  DeferCall( &aFoo, &Foo::Bar, aValue );

Some implementation details:

There is one last problem to solve: in the example code, a copy of the last parameter to DeferCall is made and stored inside the wrapper object. Function Foo::Bar thus works with a copy made at the time the call was deferred. This is not always appropriate since aValue changes before the call is actually executed and it might be necessary to use the most up-to-date value.

There are two different solutions for this problem. First, there is a relatively simple solution. It provides complete control over how the originally passed arguments are stored but at the same time also imposes some restrictions on what kind of arguments can be passed to an asynchronously called function (see Technicalities). The second solution is more general. It does not have such limitations, but complicates the syntax a little bit. For the sake of simplicity I show the first solution:

template< class Type >
class Referencer
{
  public:
    Referencer( Type & rType ) 
      : rType( rType ) {}

    operator Type &() const 
    { return rType; }

  private:
    Type & rType;
};

template< class Type >
inline Referencer< Type > Reference(
  Type & rType
)
{
  return Referencer< Type >( rType );
}

When the following line of code is used to generate a deferred call, Foo::Bar works with the most up-to-date value:

Functor * pCall =
  DeferCall( &aFoo, &Foo::Bar,
    Reference( aValue ) );

Referencer< float > stores a reference to the float value passed to its constructor. The conversion operator later allows Referencer< float > to be implicitly converted to a float reference. From this the compiler generates a temporary instance of an int, which is finally passed to Foo::Bar. That’s it. In the online code, you will find several examples including a multithreaded test program for Microsoft’s Visual Studio 6.0.

Technicalities

Conclusion

With just one line of C++ code, any kind of function call can be put together and wrapped into a Functor object. This is done in a completely type-safe manner; that is, any type mismatches are flagged with an error at compile time. The biggest application for this technique is in multithreaded programs where cross-thread communication is often realized with asynchronous calls. The calling thread uses DeferCall to wrap up the call and pushes the returned Functor object into a queue. The worker thread later pops the object out of the queue, calls Functor::operator() and deletes the object.

Notes and References

[1] Erich Gamma, Richard Helm, Ralph Johnson and John Vlissides. Design Patterns: Elements of Reusable Object-Oriented Software (Addison-Wesley, 1995).

[2] Andrei Alexandrescu. Modern C++ Design: Generic Programming and Design Patterns Applied (Addison-Wesley, 2001). See the chapter, “Generalized functors.”

[3] Smart pointers with pointer-to-member operators are possible, but not yet very common. For a good discussion on this topic, see Scott Meyers’ article, “Implementing operator->* for Smart Pointers,” Dr. Dobb’s Journal, October 1999. Also available at http://www.ddj.com/articles/1999/9910/9910b/9910b.htm.

[4] Bjarne Stroustrup. The C++ Programming Language, Third Edition (Addison-Wesley, 1997), pp. 418-421.

Acknowledgements

First and foremost, I would like to thank Herb Sutter and Marc Briand. This article would be considerably less readable without their help during the publishing process. Thanks also to Andrei Alexandrescu, who was so kind to send me his then not yet published work on Generalized Functors, which turned out to have a lot of similarities with what I was doing. Andrei offers sort of an extremely flexible and STL-compatible callback mechanism, which happens to cover all the issues discussed under option no. 2) and 3), and a lot more. His work greatly helped me to streamline and generalize the code for this article. Last but not least, thanks to Philipp Sutter, Jörg Keller, Simon Brülisauer and Michael Koster of Zühlke Engineering for further reviews and comments.

Andreas Huber is a software engineer at Zühlke Engineering AG (www.zuehlke.com) based in Zurich, Switzerland. He has seven years of experience in software development with C++ on the Windows NT/2000 platform. He specializes in generic and object-oriented programming, software architecture, and consulting. He may be contacted at andreas.huber@zuehlke.com or andreas@huber.net.