c++concurrencyc++11mutexsemaphore

Use a mutex as a semaphore?


I need two threads to progress in a "tick tock" pattern. When implmented with a semaphore this looks fine:

Semaphore tick_sem(1);
Semaphore tock_sem(0);

void ticker( void )
{
   while( true )
   {
      P( tick_sem );
      do_tick();
      V( tock_sem );
   }
}

void tocker( void )
{
   while( true )
   {
      P( tock_sem );
      do_tock();
      V( tick_sem );
   }
}

However, if I do the same thing with a mutex ( which is technically a binary semaphore ), it has an odd code smell.

std::mutex tick_mutex;
std::mutex tock_mutex;
tock_mutex.lock();

void ticker( void )
{
   while( true )
   {
      tick_mutex.lock();
      do_tick();
      tock_mutex.unlock();
   }
}

void tocker( void )
{
   while( true )
   {
      tock_mutex.lock()
      do_tock();
      tick_mutex.unlock();
   }
}

I think the smell is that a mutex isn't meant to convey information to another thread. (The c++11 standard committee added a spurious fail to try_lock to defeat unexpected information transfer; §30.4.1/14.) It seems like mutexes are meant to synchronize access to a variable, which can then convey information to another thread.

Lastly, when implemented with a std::condition_variable, it looks correct but it's more complicated ( a tick_vs_tock variable, a mutex, and a condition variable). I've omitted the implementation for brevity, but it's really straight forward.

Is the mutex solution fine? Or is there something subtly wrong with it?

Is there a good pattern for solving my tick/tock problem that I haven't thought of?


Solution

  • A Mutex is not simply just a binary semaphore, it also has the limitation that only the locking thread is allowed to unlock it.

    You are breaking that rule.

    Edit:

    From MSDN:

    The ReleaseMutex function fails if the calling thread does not own the mutex object.

    From some site that google turned up for pthread_mutex_unlock:

    The pthread_mutex_unlock() function may fail if:

    EPERM The current thread does not own the mutex.

    And you will find the same on other mutex implementations. It makes sense because a mutex is supposed to guard a thread's access to a resource, so another thread should not be able to unlock it.