Boost logo

Boost :

From: Gregory Seidman (gseidman_at_[hidden])
Date: 2000-10-23 15:25:10


A friend of mine handed me the minutes of the 10/22 meeting because I had
been telling him about having implemented monitors in C++ (trying to have
the convenience of Java's synchronization primitives). I have attached my
implementation to this message.

I want to make it clear that this was primarily implemented for my own
convenience. It is only because of the simplicity of the design and code
that I consider it reasonable to inflict on you as is. Note also that there
are numerous optimizations to be implemented. I am hoping that this will be
considered a starting point; I am not presenting it as finished code for
public consumption. I am releasing this code to the public domain. No
rights reserved (though I'd kind of like some credit for it somewhere, I
will not legally require it).

TODO:

        port to other threading libraries (win32, etc.)
                currently POSIX only
        efficient allocator for Monitor::Token
                relies on auto_ptr, thus new and delete
        improve recursive locking
                each recursive lock is two pthread calls
        add various runtime options
                size of initial Token memory pool
                tradeoff between lock speed and condition wait speed
                recursion on or off
        revise preprocessor macros
        do something smarter with Monitor destruction
        namespace changes
        improve or remove Synchronizable convenience class

I assume most everyone is familiar with the concept of monitors. The way
one uses this implementation is as follows (playing fast and loose with
namespaces):

class SomeClass {
  private:
    Monitor *mon, *othermon;
  public:
    SomeClass() : mon(new Monitor()), othermon(new Monitor()) { }
    ~SomeClass() {
      while (!(
        Monitor::destroyMonitor(mon) &&
        Monitor::destroyMonitor(othermon)
            ));
    }
    void synchronizedMethod();
};

void
SomeClass::synchronizedMethod() {
  MONITOR_SYNCHRONIZE_BLOCK(mon);
  //...early return
  if (early)
    return;
  //...recursive call
  if (notdone)
    synchronizedMethod();
  //...synchronizing a subblock
  if (otherthing) {
    MONITOR_SYNCHRONIZE_BLOCK(othermon);
    //...
  }
  //...exception
  if (exceptional)
    throw foo;
  //...normal exit
}

The locking occurs upon allocation of a Monitor::Token and unlocking occurs
when that token is deleted. By enclosing the token in an auto_ptr we
guarantee that however the stack is unwound (exception, early return,
whatever) as soon as the token is out of scope the monitor is unlocked. The
monitor is currently recursive (i.e. once in code protected by the monitor
the program can call any other code protected by the same monitor) and
works around the problem with recursive POSIX mutexes and conditional
waiting (only unlocking the recursive mutex once instead of completely).

Note that, like any other synchronization system, it is still possible to
deadlock in all the usual ways.

--Greg





Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk