CPP互斥锁异常未触发自定义处理程序,调用Watson致无崩溃转储
问题场景
我们的代码存在如下死锁问题(简化版本):
std::shared_timed_mutex read_file_mutex; std::shared_lock<std::shared_timed_mutex> read_file_lock(read_file_mutex , std::defer_lock); if (read_file_lock.try_lock_for(std::chrono::milliseconds(200))) { ... ... if (read_file_lock.try_lock_for(std::chrono::milliseconds(200))) {
第二次调用try_lock_for时,MSVC的shared_mutex在验证阶段会抛出资源死锁的异常,符合预期:
void _Validate() const { // check if the mutex can be locked if (!_Pmtx) _THROW(system_error( _STD make_error_code(errc::operation_not_permitted))); if (_Owns) _THROW(system_error( _STD make_error_code(errc::resource_deadlock_would_occur))); }
核心疑问
当上述异常触发时,为何会调用Watson处理程序而非我们注册的自定义无效参数异常处理程序?这导致程序终止且未生成崩溃转储。
已做的异常处理注册
我们已按文档注册了多种异常处理程序,其他类型异常均能正常触发自定义处理(在处理程序中生成崩溃转储),示例代码如下:
_invalid_parameter_handler old_handler, new_handler; SetUnhandledExceptionFilter(&UnhandledExceptionHandler); new_handler = HandleInvalidParameter; old_handler = _set_invalid_parameter_handler(new_handler); _set_purecall_handler(PureVirtualCallHandler); void (*rv)(int) = signal(SIGABRT, HandleAbortSignal); _set_abort_behavior(0, 0); EnableCrashingOnCrashes(); PreventSetUnhandledExceptionFilter();
环境说明
- 32位软件运行于64位Windows 10系统
- 编译器为clang
原因分析
- 异常类型不匹配:
_set_invalid_parameter_handler仅处理C标准库或MSVC特定的无效参数异常(比如printf传入无效格式符触发的异常),而std::system_error属于C标准库抛出的**C异常**,不属于前者的处理范畴。 - 未捕获C++异常的默认行为:未捕获的
std::system_error会触发std::terminate,默认情况下std::terminate会调用abort()触发SIGABRT信号,但如果STL内部抛出异常时直接绕过了自定义信号处理逻辑,就会触发Watson崩溃报告。 - Clang+MSVC STL的特殊行为:使用Clang编译但链接MSVC标准库时,部分STL内部抛出异常的路径可能直接调用Windows系统的崩溃处理逻辑,而非走你注册的自定义异常处理链。
解决方法
- 主动捕获C++异常:在可能抛出
std::system_error的代码段外层添加try-catch块,主动捕获并触发自定义崩溃转储逻辑:
try { if (read_file_lock.try_lock_for(std::chrono::milliseconds(200))) { // ...业务代码 if (read_file_lock.try_lock_for(std::chrono::milliseconds(200))) { // ... } } } catch (const std::system_error& e) { // 调用自定义崩溃转储逻辑 UnhandledExceptionHandler(nullptr); }
- 替换std::terminate处理函数:通过
std::set_terminate注册自定义终止处理程序,覆盖默认的std::terminate行为:
void CustomTerminateHandler() { // 执行崩溃转储逻辑 UnhandledExceptionHandler(nullptr); // 确保程序终止 abort(); } // 在程序初始化阶段注册 std::set_terminate(CustomTerminateHandler);
- 修复死锁根源:最根本的方式是避免重复锁操作,在调用
try_lock_for前检查锁状态:
if (read_file_lock.try_lock_for(std::chrono::milliseconds(200))) { ... ... // 仅在未持有锁时尝试加锁 if (!read_file_lock.owns_lock() && read_file_lock.try_lock_for(std::chrono::milliseconds(200))) { // ... } }
内容的提问来源于stack exchange,提问作者device_exec
相关产品推荐
相关产品推荐

