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?
Myth: Mutexes and Semaphores are Interchangeable The most common (but nonetheless incorrect) answer to the question posed at the top is that mutexes and semaphores are very similar, with the only significant difference being that semaphores can count higher than one.
If you have number of instances for resource it is better to use Binary semaphore. If you have single instance for resource it is better to use mutex.
The mutex is used for protecting parts of code from running concurrently, semaphores are used for one thread to signal another thread to run.
Mutexes can synchronize threads within the same process or in other processes. Mutexes can be used to synchronize threads between processes if the mutexes are allocated in writable memory and shared among the cooperating processes (see mmap(2)), and have been initialized for this task.
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.
Since you have a case to use a semaphore, I think the fix is to portably implement one using a mutex and a condition variable.
This might not be especially efficient (since it'll use a mutex/condvar pair per semaphore), but you can switch in an alternate implementation on systems that have their own semaphores (such as Posix and Windows).
Apparently semaphores are "too error-prone". With all due respect to Boost, I think at least some of us can manage. Certainly you can tie yourself in knots trying to do complicated things with multiple semaphores, and they are a pretty low-level tool. But when they're the right thing, no problem.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With