使用std::async的简单C++程序为何崩溃?
代码崩溃问题分析与修复
背景
在排查AIX平台使用XLClang++时__cxa_end_catch崩溃问题的过程中,尝试重构代码以避免使用std::exception_ptr,代码如下:
#include <cstdio> #include <exception> #include <future> #include <thread> #include <vector> std::future<std::invalid_argument> GetExceptionFromDeadThread() { return std::async(std::launch::async, []() { try { throw std::invalid_argument("Some string long enough to allocate on the heap? "); } catch (std::invalid_argument e) { return e; } catch (...) { std::fprintf(stderr, "'std::rethrow_exception(future.get())' threw unexpected exception"); abort(); } }); } int main() { try { constexpr size_t numThreads(100); std::vector<std::future<std::invalid_argument>> futures; while (true) { for (size_t i = 0; i < numThreads; ++i) { futures.push_back(GetExceptionFromDeadThread()); } while (!futures.empty()) { auto& future(futures.back()); future.wait(); try { throw future.get(); } catch (std::invalid_argument&) { std::fputs(".", stdout); } catch (...) { std::fprintf(stderr, "'std::rethrow_exception(future.get())' threw unexpected exception"); abort(); } futures.pop_back(); } } } catch (...) { std::fprintf(stderr, "Caught unexpected exception"); abort(); } }
测试崩溃情况
在Windows开发机(Visual Studio 2022,默认创建的C++控制台项目,Debug|x64配置)测试时,程序出现崩溃,报错信息:
HEAP[exception_ptr_test.exe]: Invalid address specified to
RtlValidateHeap( 0000021A37BB0000, 00007FFFDA43A2F0 )
调用栈信息:
> ntdll.dll!RtlpBreakPointHeap() Unknown ntdll.dll!RtlpValidateHeapEntry() Unknown ntdll.dll!RtlValidateHeap() Unknown KernelBase.dll!HeapValidate() Unknown ucrtbased.dll!_CrtIsValidHeapPointer(const void * block) Line 1407 C++ ucrtbased.dll!free_dbg_nolock(void * const block, const int block_use) Line 904 C++ ucrtbased.dll!_free_dbg(void * block, int block_use) Line 1030 C++ ucrtbased.dll!free(void * block) Line 32 C++ vcruntime140d.dll!__std_exception_destroy(__std_exception_data * data) Line 46 C++ exception_ptr_test.exe!std::exception::~exception() Line 91 C++ exception_ptr_test.exe!std::logic_error::~logic_error() C++ exception_ptr_test.exe!std::invalid_argument::~invalid_argument() C++ exception_ptr_test.exe!std::_Packaged_state<std::invalid_argument __cdecl(void)>._Call_immediate() Line 494 C++ exception_ptr_test.exe!std::_Task_async_state<std::invalid_argument>::{ctor}::__l2::<lambda_1>::operator()() Line 664 C++ exception_ptr_test.exe!std::invoke<`std::_Task_async_state<std::invalid_argument>::_Task_async_state<std::invalid_argument><std::_Fake_no_copy_callable_adapter<`GetExceptionFromDeadThread'::`2'::<lambda_1>>>.'::`2'::<lambda_1> &>(std::_Task_async_state<std::invalid_argument>::{ctor}::__l2::<lambda_1> & _Obj) Line 1695 C++ exception_ptr_test.exe!std::_Func_impl_no_alloc<`std::_Task_async_state<std::invalid_argument>::_Task_async_state<std::invalid_argument><std::_Fake_no_copy_callable_adapter<`GetExceptionFromDeadThread'::`2'::<lambda_1>>>.'::`2'::<lambda_1>,void>._Do_call() Line 874 C++ exception_ptr_test.exe!std::_Func_class<void>::operator()() Line 920 C++ exception_ptr_test.exe!Concurrency::details::_MakeVoidToUnitFunc::__l2::<lambda_1>::operator()() Line 2363 C++ exception_ptr_test.exe!std::invoke<`Concurrency::details::_MakeVoidToUnitFunc'::`2'::<lambda_1> &>(Concurrency::details::_MakeVoidToUnitFunc::__l2::<lambda_1> & _Obj) Line 1696 C++ exception_ptr_test.exe!std::_Func_impl_no_alloc<`Concurrency::details::_MakeVoidToUnitFunc'::`2'::<lambda_1>,unsigned char>._Do_call() Line 878 C++ exception_ptr_test.exe!std::_Func_class<unsigned char>::operator()() Line 921 C++ exception_ptr_test.exe!Concurrency::task<unsigned char>::_InitialTaskHandle<void,`std::_Task_async_state<std::invalid_argument>::_Task_async_state<std::invalid_argument><std::_Fake_no_copy_callable_adapter<`GetExceptionFromDeadThread'::`2'::<lambda_1>>>.'::`2'::<lambda_1>,Concurrency::details::_TypeSelectorNoAsync>._LogWorkItemAndInvokeUserLambda<std::function<unsigned char __cdecl(void)>>(std::function<unsigned char __cdecl(void)> _func) Line 3528 C++ exception_ptr_test.exe!Concurrency::task<unsigned char>::_InitialTaskHandle<void,`std::_Task_async_state<std::invalid_argument>::_Task_async_state<std::invalid_argument><std::_Fake_no_copy_callable_adapter<`GetExceptionFromDeadThread'::`2'::<lambda_1>>>.'::`2'::<lambda_1>,Concurrency::details::_TypeSelectorNoAsync>._Init(Concurrency::details::_TypeSelectorNoAsync __formal) Line 3548 C++ exception_ptr_test.exe!Concurrency::task<unsigned char>::_InitialTaskHandle<void,`std::_Task_async_state<std::invalid_argument>::_Task_async_state<std::invalid_argument><std::_Fake_no_copy_callable_adapter<`GetExceptionFromDeadThread'::`2'::<lambda_1>>>.'::`2'::<lambda_1>,Concurrency::details::_TypeSelectorNoAsync>._Perform() Line 3533 C++ exception_ptr_test.exe!Concurrency::details::_PPLTaskHandle<unsigned char,Concurrency::task<unsigned char>::_InitialTaskHandle<void,`std::_Task_async_state<std::invalid_argument>::_Task_async_state<std::invalid_argument><std::_Fake_no_copy_callable_adapter<`GetExceptionFromDeadThread'::`2'::<lambda_1>>>.'::`2'::<lambda_1>,Concurrency::details::_TypeSelectorNoAsync>,Concurrency::details::_TaskProcHandle>::invoke() Line 1475 C++ exception_ptr_test.exe!Concurrency::details::_TaskProcHandle::_RunChoreBridge(void * _Parameter) Line 171 C++ exception_ptr_test.exe!Concurrency::details::_DefaultPPLTaskScheduler::_PPLTaskChore::_Callback(void * _Args) Line 57 C++ msvcp140d.dll!Concurrency::details::`anonymous namespace'::_Task_scheduler_callback(_TP_CALLBACK_INSTANCE * _Pci, void * _Args, _TP_WORK * __formal) Line 134 C++ ntdll.dll!TppWorkpExecuteCallback() Unknown ntdll.dll!TppWorkerThread() Unknown kernel32.dll!BaseThreadInitThunk() Unknown ntdll.dll!RtlUserThreadStart() Unknown

修复方法
将代码中catch (std::invalid_argument e)块内的return e改为return std::invalid_argument(e)后,崩溃问题消失。
内容的提问来源于stack exchange,提问作者Bwmat
相关产品推荐
相关产品推荐

