【问题标题】:Boost shared mutex not released after exception thrown引发异常后未释放提升共享互斥锁
【发布时间】:2014-05-17 09:34:43
【问题描述】:

我在调用 C++ 库的现有 .NET (C#, 3.5) 应用程序中遇到了一个奇怪的 Boost (v1.38) 互斥锁死锁。在获得读取锁之后的某个时间点 [正确] 抛出异常,并且该异常一直未处理,一直返回到托管 .NET 代码(处理它的位置)。尝试使用 setter 方法的下一次对 c++ 库的调用无限期地挂在唯一锁获取上(推测读取锁未释放):

ntdll.dll!NtWaitForSingleObject() + 0x15 bytes 
kernel32.dll!WaitForSingleObjectEx() + 0x43 bytes 
kernel32.dll!WaitForSingleObject() + 0x12 bytes 
OurCPPLib.dll!boost::shared_mutex::unlock_upgrade_and_lock() Line 478 + 0x11 bytes C++ 
OurCPPLib.dll!boost::unique_lock<boost::shared_mutex>::unique_lock<boost::shared_mutex>(boost::detail::thread_move_t<boost::upgrade_lock<boost::shared_mutex> > other) Line 788 C++ 
OurCPPLib.dll!boost::upgrade_to_unique_lock<boost::shared_mutex>::upgrade_to_unique_lock<boost::shared_mutex>(boost::upgrade_lock<boost::shared_mutex> & m_) Line 802 + 0x98 bytes C++ 
OurCPPLib.dll!OurClass::SetSomething(double something) Line 95 C++ 

该类定义了许多 Get 和 Set 方法(读取器/写入器)并像这样实现它们:

boost::shared_mutex _calcSharedMutex;

RETURNCODE GetSomething(double& something)
{
    boost::shared_lock<boost::shared_mutex> lock(_calcSharedMutex);
    return _anotherObject->GetSomething(something);
}

RETURNCODE SetSomething(double something)
{
    boost::upgrade_lock<boost::shared_mutex> lock(_calcSharedMutex);
    boost::upgrade_to_unique_lock<boost::shared_mutex> uniqueLock(lock);
    return _anotherObject->SetSomething(something);
}

调用 _anotherObject->GetSomething() 会在极少数情况下抛出异常:

throw std::invalid_argument("Unknown something");

此外,getter 中有一些调用 _anotherObject->GetSomething() 是在 C++ 库本身的 try/catch 中进行的,从而防止异常返回到托管代码,并且不会导致这种死锁.未处理的异常是否会破坏 boost mutex 范围的解锁?

提前感谢任何可能有一些见解的人!

【问题讨论】:

  • 这段代码是用 /clr 编译的吗?如果是,您是否在其前面添加了#pragma managed(push, off)?
  • c++ 没有使用 /clr 开关编译。
  • 这很奇怪,这应该可以。除了实际显示问题的演示程序之外,您唯一的其他选择是使用 /EHa 编译该 C++ 代码。尽管 that 会起作用是没有意义的。

标签: c# c++ boost mutex deadlock


【解决方案1】:

在 C++ 中未指定当抛出未处理的异常时是否展开堆栈。一些实现这样做(调用析构函数和应该发生的所有其他事情),而另一些则没有。我不确切知道 C++/CLI 是如何处理这个问题的,但是如果 C++ 部分将异常视为未处理,那么它可能不会展开堆栈,因此不会调用析构函数并释放互斥锁.

(如果是这样,只需在 C++ 代码中捕获并重新抛出异常即可解决问题)

但这只是猜测。我从来没有经常使用 C++/CLI,而且我不知道异常是如何在本机代码和托管代码之间传播的。

【讨论】:

    【解决方案2】:

    2.0 CLR 中存在一个错误,当在托管代码中处理原生异常时,该错误会阻止堆栈展开。

    Microsoft Connect: /Ehsc & /Eha & stack unwinding

    将托管可执行文件切换为在 4.0 CLR 上运行更正了该问题。

    附带说明,本机 C++ 库是从一个面向 2.0 CLR 的托管 C# 库调用的。托管程序集可以继续以 2.0 CLR 为目标,因为它将在可执行文件 (4.0) 的无错误 CLR 上执行,但它将在 2.0 兼容模式下执行。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-17
      • 2010-12-25
      • 2012-02-23
      • 2021-10-10
      • 2012-06-29
      • 1970-01-01
      相关资源
      最近更新 更多