2011-08-17 49 views
0

我在一个预先调用C++库的.NET(C#,3.5)应用程序中遇到了一个奇怪的Boost(v1.38)互斥体死锁。在获得读取锁定之后,某个点会[正确]抛出异常,并且该异常一直回到受管理的.NET代码(处理该代码的位置)。在C++库中的下一个呼叫尝试使用setter方法上唯一的锁AQUISITION挂起无限期地(大概是读锁未释放):在抛出异常后未释放Boost共享互斥体

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"); 

此外, re是getter中的一些对C++库本身的try/catch中的_anotherObject-> GetSomething()进行的调用,防止异常返回到托管代码,并且不会导致此死锁。未处理的异常是否破坏了互斥锁范围的解锁?

在此先感谢任何可能有所洞察的人!

+0

这段代码是用/ clr生效的吗?如果是这样,你是否在它之前放置了#pragma managed(push,off)? –

+0

未使用/ clr开关编译C++。 – roken

+0

这很奇怪,这应该工作。除了实际显示问题的演示程序之外,您唯一的选择是使用/ EHa编译该C++代码。尽管* that *没有任何意义。 –

回答

0

在2.0 CLR中存在一个错误,它可以防止在托管代码中处理本机出生的异常时解除堆栈。

Microsoft Connect: /Ehsc & /Eha & stack unwinding

切换托管可执行文件运行在4.0 CLR纠正问题。

作为一个方面说明,本地C++库是从托管的C#库调用的,该C#库针对2.0 CLR。托管程序集可以继续以2.0 CLR为目标,因为它将在可执行文件(4.0)的无bug CLR上执行,但它将以2.0兼容性模式执行。

2

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

(如果是这样,只需捕获和重新抛出的C++代码异常应解决的问题)

但是,这只是一个猜测。我从来没有用过C++/CLI,我也不知道如何在本地代码和托管代码之间传播异常。