2011-08-17 81 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,我也不知道如何在本地代碼和託管代碼之間傳播異常。