I have a multithreaded application (using std::thread) with a manager (class Tree) that executes some piece of code on different subtrees (embedded struct SubTree) in parallel. The basic idea is that each instance of SubTree has a deque that store objects. If the deque is empty, the thread waits until a new element is inserted in the deque or the termination criteria is reached. One subtree can generate objects and push them in the deque of another subtree. For convenience, all my std::mutex, std::locks and std::variable_condition are stored in a struct called "locks".
The class Tree creates some threads that run the following method (first attempt) :
void Tree::launch(SubTree & st, Locks & locks )
{
/* some code */
std::lock_guard<std::mutex> deque_lock(locks.deque_mutex_[st.id_]) ; // lock the access to the deque of subtree st
if (st.deque_.empty()) // check that the deque is still empty
{
// some threads are still running, wait for them to terminate
std::unique_lock<std::mutex> wait_lock(locks.restart_mutex_[st.id_]) ;
locks.restart_condition_[st.id_].wait(wait_lock) ;
}
/* some code */
}
The problem is that "deque_lock" is still locked while the thread is waiting. Hence no object can be added in the deque of the current thread by a concurrent one.
So I turned the lock_guard into a unique_lock and managed the lock/unlock manually :
void launch(SubTree & st, Locks & locks )
{
/* some code */
std::unique_lock<std::mutex> deque_lock(locks.deque_mutex_[st.id_]) ; // lock the access to the deque of subtree st
if (st.deque_.empty()) // check that the deque is still empty
{
deque_lock.unlock() ; // unlock the access to the deque to enable the other threads to add objects
// DATA RACE : nothing must happen to the unprotected deque here !!!!!!
// some threads are still running, wait for them to terminate
std::unique_lock<std::mutex> wait_lock(locks.restart_mutex_[st.id_]) ;
locks.restart_condition_[st.id_].wait(wait_lock) ;
}
/* some code */
}
The problem now, is that there is a data race, and I would like to make sure that the "wait" instruction is performed directly after the "deque_lock.unlock()" one. Would anyone know a way to create such a critical instruction sequence with the standard library ?
Thanks in advance.