我在预先存在的.NET(C#,3.5)应用程序中遇到了一个奇怪的Boost(v1.38)互斥锁死锁,该应用程序调用C ++库。在获得读取锁定之后的某个时刻[正确]抛出异常,并且该异常未处理完全一直返回到托管.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-&gt; GetSomething()的调用将在极少数情况下抛出异常:
throw std::invalid_argument("Unknown something");
此外,在getter中有一些调用_anotherObject-&gt; GetSomething()是在C ++库本身的try / catch中进行的,防止异常返回到托管代码,这不会导致僵局。未处理的异常是否会破坏boost mutex范围的解锁?
提前感谢任何可能有所了解的人!
答案 0 :(得分:2)
在C ++中未指定在抛出未处理的异常时是否展开堆栈。一些实现这样做(调用析构函数和其他应该发生的事情),而其他实现则不这样做。我不确切知道C ++ / CLI如何处理这个问题,但是如果C ++部分将异常视为未处理,那么它可能不会展开堆栈,因此不会调用析构函数并释放互斥锁
(如果是这样,只需捕获并重新抛出C ++代码中的异常即可解决问题)
但这只是猜测。我从未使用过很多C ++ / CLI,而且我也不知道如何在本机代码和托管代码之间传播异常。
答案 1 :(得分:0)
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兼容模式执行。