Повышенный общий мьютекс не освобождается после выброса исключения
Я натолкнулся на странную тупиковую взаимоблокировку Boost (v1.38) в ранее существовавшем приложении.NET (C#, 3.5), которое выполняет вызовы библиотеки C++. Исключение [правильно] генерируется в точке после получения блокировки чтения, и это исключение остается необработанным вплоть до управляемого кода.NET (где он обрабатывается). Следующий вызов библиотеки 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 (), которые выполняются в try / catch в самой библиотеке C++, предотвращая возвращение исключения в управляемый код, и это не вызывает эту тупиковую ситуацию. Не нарушает ли необработанное исключение разблокировку буст-мьютекса?
Заранее спасибо всем, кто может иметь некоторое понимание!
2 ответа
В 2.0 CLR есть ошибка, которая не позволяет стеку раскручиваться при обработке собственного исключения в управляемом коде.
Microsoft Connect: / Ehsc & / Eha & разматывание стека
Переключение управляемого исполняемого файла для запуска на 4.0 CLR устранило проблему.
Как примечание, нативная библиотека C++ вызывается из управляемой библиотеки C#, которая ориентирована на 2.0 CLR. Управляемая сборка может продолжать ориентироваться на 2.0 CLR, поскольку она будет выполняться на CLR без ошибок исполняемого файла (4.0), но будет выполняться в режиме совместимости 2.0.
В C++ не указано, будет ли стек разматываться при возникновении необработанного исключения. Некоторые реализации делают это (вызывая деструкторы и все остальное, что должно произойти), в то время как другие этого не делают. Я не знаю точно, как C++/CLI обрабатывает это, но если часть C++ видит исключение как необработанное, то возможно, что оно не размотает стек и, таким образом, не вызовет деструктор и не освободит мьютекс,
(если это так, просто перехват исключения и пересылка в коде C++ должен решить проблему)
Но это всего лишь предположение. Я никогда не использовал C++/CLI, и я понятия не имею, как распространяются исключения между нативным и управляемым кодом.