views:

329

answers:

1

Suppose I write a C++ semaphore class with an interface that models the boost Lockable concept (i.e. lock(); unlock(); try_lock(); etc.). Is it safe/recommended to use boost locks for RAII access to such an object? In other words, do boost locks (and/or other related parts of the boost thread library) assume that the Lockable concept will only be modeled by mutex-like objects which are locked and unlocked from the same thread?

My guess is that it should be OK to use a semaphore as a model for Lockable. I've browsed through some of the boost source and it "seems" OK. The locks don't appear to store explicit references to this_thread or anything like that. Moreover, the Lockable concept doesn't have any function like whichThreadOwnsMe(). It also looks like I should even be able to pass a boost::unique_lock<MySemaphore> reference to boost::condition_variable_any::wait. However, the documentation is not explicitly clear about the requirements.

To illustrate what I mean, consider a bare-bones binary semaphore class along these lines:

class MySemaphore{
  bool locked;
  boost::mutex mx;
  boost::condition_variable cv;
public:
  void lock(){
    boost::unique_lock<boost::mutex> lck(mx);
    while(locked) cv.wait(lck);
    locked=true;
  }

  void unlock(){
    {
      boost::lock_guard<boost::mutex> lck(mx);
      if(!locked) error();
      locked=false;
    }
    cv.notify_one();
  }
// bool try_lock(); void error(); etc.
}

Now suppose that somewhere, either on an object or globally, I have

MySemaphore sem;

I want to lock and unlock it using RAII. Also I want to be able to "pass" ownership of the lock from one thread to another. For example, in one thread I execute

void doTask()
{
  boost::unique_lock<MySemaphore> lock(sem);
  doSomeWorkWithSharedObject();
  signalToSecondThread();
  waitForSignalAck();
  lock.release();
}

While another thread is executing something like

{
waitForSignalFromFirstThread();
ackSignal();
boost::unique_lock<MySemaphore>(sem,boost::adopt_lock_t());
doMoreWorkWithSameSharedObject();
}

The reason I am doing this is that I don't want anyone else to be able to get the lock on sem in between the time that the first thread executes doSomeWorkWithSharedObject() and the time the second executes doMoreWorkWithSameSharedObject(). Basically, I'm splitting one task into two parts. And the reason I'm splitting the task up is because (1) I want the first part of the task to get started as soon as possible, (2) I want to guarantee that the first part is complete before doTask() returns, and (3) I want the second, more time-consuming part of the task to be completed by another thread, possibly chosen from a pool of slave threads that are waiting around to finish tasks that have been started by master threads.

NOTE: I recently posted this same question (sort of) here http://stackoverflow.com/questions/2754884/unlocking-a-mutex-from-a-different-thread-c but I confused mutexes with semaphores, and so the question about using boost locks didn't really get addressed.

+1  A: 

@dan, I think you are overcomplicating things. What you are describing is easily achievable with a main processing thread, a synchronized queue, and a [pool of] worker thread(s). It also looks like you are falling into a common trap of using locks to "protect code", while it's the data structures you need to protect.

Define your shared data, identify minimal critical sections when the data might be inconsistent. Brace that with locks.

Nikolai N Fetissov