C++ Blog

Shared Memory based C++ programming

Posted in boost by Umesh Sirsiwal on January 5, 2009

When it comes to shared memory based programming, we C++ users have traditionally found our selves at disadvantage. Most our beloved constructs like shared pointers, various containers, scoped locks, etc. don’t work very well when programming in C++. In addition, we cannot place objects which use inheritance in shared memory since the virtual pointers cannot work in shared memory. These limitation has implied that the shared memory is rarely used in C++ programming. Whenever it is used, the object placed in the shared memory are more like C constructs rather then C++ constructs. This is pity since using shared memory provides significant performance advantage in certain conditions.

Boost 1.35 includes Interproces which provides C++ based programming environment for shared memory. The facilities provided are:

  • Shared memory based pool of memory and capability to new C++ objects from this pool
  • Mutexes placed in the shared memory
  • Various smart pointers
  • Various STL-like containers which can be placed in shared memory. These containers include maps, deque, set, list, flat_map, flat_set, slist, and string

In addition various boost containers can be placed in shared memory. These containers include the multi-index container.

Hopefully these constructs make placing objects in shared memory a little easier for you all. Please note that placing data in shared memory has traditionally been dangerous due to complexity in debugging crashes, not easily understood deadlocks, etc. Please use shared memory based containers carefully.

Advertisements

C++ Monitor Pattern

Posted in boost by Umesh Sirsiwal on January 5, 2009

Locking is an integral part of concurrent programming. A usual class used with threads looks like:

class A {
void F() {
ScopedLock l(m);
....
....
}
Void G()
{
ScopedLock l(m);
....
....
}
mutex m;
};
This guarantees that the class data part of class A are always protected from multiple thread access. However, this code is fragile and difficult to maintain. It moves the responsibility of maintaining concurrency to the class writer. Also, sooner or later somebody will make a change of the following form:

Void G()
{
ScopedLock l(m);
....
....
F();
}

This will deadlock unless you were using recursive mutex.

Also, a construct like this is difficult to use if you are trying to use third party library like STL. For example, let us say you were using std::map. In order to enforce appropriate concurrency control you must wrap every map method you are going to use with a lock that implies you must implement a forwarding class which takes the user argument performs a lock and then call the map function. This is tedious, time consuming and absolute waste of time.

Won’t it be nice if there was a way to generically wrap class and provided method forwarders? The forwarder will provide the lock/unlock capability and the class implementer will not have to worry about concurrency issues. Welcome to monitor pattern. This pattern was first introduced in this paper by Douglas Schmidt.The monitor object guarantees that only one method runs within an object at any given point of time. Because the monitor pattern guarantees that there is any need to perform locking within class methods.

One of the C++ monitor pattern implementation is part of libpoet. ACE provides another implementation of the monitor pattern. I am sure there are other implementations. The libpoet implementation is partially based on the this paper by Bjarne Stroustrup. The library includes two implementations of the monitor pattern. One is based on smart pointer and the other is based on inheritance and has additional functionality. In most cases the pointer based implementation is sufficient and is considered in this post.

The monitor pointer is implemented as smart pointer and is called monitor_ptr. Like any other smart pointer it provides an object wrapper. The wrapper in this case takes a lock before the object is called and releases the lock once the object call is complete.  To use the class A defined above with monitor_ptr we will remove the locks from the class and wrap A as follows:

poet::monitor_ptr<A>  a(new A);

Now calls to  a->F() and a->G() will result in automatic locks taken by the monitor_ptr and call to G() will wait till call to F is complete.

class A {
void F() {
....
....
}
Void G()
{
....
....
}
};
Look no locks. If we need to use a map with concurrent programming we will write something like:

poet::monitor_ptr< std::map<int, std::string> > m(new  std::map<int, std::string>);

We will now access the monitored map using something like:

m->insert(1,std::string("abc"));

without worrying about the locking.

The inheritance based implementation provides additional functionality since it can use condition wait constructs.

Of course it will be much nicer to have a language level support (similar to synchronized keyword in Java) for the monitor pattern. That is a lot more elegant but till the language supports it we will have to live with the smart pointer based implementation.