2014-03-04 34 views
4

我正在寻找一些错误,我们有一些凌乱的线程/条件变量类被更新为使用C++ 11线程。在追捕过程中,我遇到的GCC代码库执行以下操作:GCC C++ 11条件变量等待内部

template<typename _Lock> 
    void 
    wait(_Lock& __lock) 
    { 
    unique_lock<mutex> __my_lock(_M_mutex); 
    _Unlock<_Lock> __unlock(__lock); 
    // _M_mutex must be unlocked before re-locking __lock so move 
    // ownership of _M_mutex lock to an object with shorter lifetime. 
    unique_lock<mutex> __my_lock2(std::move(__my_lock)); 
    _M_cond.wait(__my_lock2); 
    } 

尽管评论,我难以理解的举动构造的目的,在这里__my_lock2。为什么__my_lock在这里移动到__my_lock2?

+2

我怀疑它与'__my_lock'析构函数在**'__unlock'之后被调用**有关,而'__my_lock2'将首先被销毁。它基本上是围绕FILO生命周期规则进行黑客攻击。 – pmdj

回答

10

这看起来不像condition_variable的代码。它看起来像condition_variable_any的代码。后者具有模板等待功能。前者不。

N2406可能会说明您所看到的内容。在N2406中,condition_variable_any改为gen_cond_var,但其他情况下,类型相同。本文描述了一种死锁情况,在这种情况下,除非非常小心地确保_M_mutex__lock以非常特定的顺序锁定和解锁,否则会导致死锁。

虽然您展示的代码和N2406的代码不一样,但我强烈怀疑它们都是按照顺序锁定和解锁这两个锁,以避免N2406中描述的死锁。如果没有进一步了解_Unlock的定义,我不能绝对确定。然而一个强有力的线索是在这个等待函数的N2406中的最终评论:

} // mut_.unlock(), external.lock() 

即,在锁定外部__lock之前,必须解锁成员互斥锁。通过移动__my_lock与范围小于的__unlock一个局部变量,它是很有可能的

} // _M_mutex.unlock(), __lock.lock() 

正确的顺序已经实现。为了理解为什么这个顺序非常重要,你必须阅读N2406的相关部分(它可以防止死锁)。

1

它看起来像重新排列对__lock__my_lock变量的销毁。

的呼声应该是这样的:

construct __my_lock // locks _M_mutex 
construct __unlock // unlocks __lock 
construct __my_lock2 // Does nothing as its a move. 

_M_cond.wait(__my_lock2); 

destroy __mylock2 // unlocks __M_mutex 
destroy __unlock // locks __lock again 
destroy __mylock // does nothing as its been moved 

不具备转移的顺序将是

construct __my_lock // locks _M_mutex 
construct __unlock // unlocks __lock 

_M_cond.wait(__my_lock); 

destroy __unlock // locks __lock 
destroy __mylock // unlocks _M_mutex 

这可能会导致死锁在对方的回答

3

功能提到(我认为这是std::condition_variable_any<_Lock>::wait)通过建立一个锁定顺序不变量来避免死锁,因为__lock必须被锁定e获取_M_mutex。虽然这样做,它仍然必须保证在等待内部条件变量_M_cond时不会保持__lock。请注意0​​是RAII 解锁对象。它通过在其构造函数中解锁,并在解析器中锁定执行与通常锁守相反的功能。事件的必要的排序是:

  1. 获取__lock(该wait通话的前置条件)
  2. 获取_M_mutex(在__my_lock构造函数)
  3. 释放__lock(在__unlock构造函数)
  4. 原子发布_M_mutex并等待_M_cond(发生在_M_cond.wait
  5. 重新获取_M_mutex关于wa Keup酒店(也_M_cond.wait
  6. 释放_M_mutex(在__my_lock2析构函数)
  7. 重新获得__lock(在__unlock析构函数)

__my_lock__my_lock2此举是必要的,这样__my_lock2__unlock之前被销毁确保事件6发生在7之前而不是之后。