-
-
Notifications
You must be signed in to change notification settings - Fork 56.5k
Deadlock on application exit in multi-threaded environment #19875
Description
System information (version)
- OpenCV => 4.5.1 (commit 1363496)
- Operating System / Platform => Windows 10
- Compiler => Visual Studio 16.9.2 / Microsoft (R) C/C++ Optimizing Compiler Version 19.28.29913 for x64
Detailed description
We have an application that uses multiple threads. Some of them are managed by a singleton object which is destroyed using an atexit() handler. This leads to a deadlock situation with OpenCV.
Call stack of the main thread (id 126036):
[Waiting on Thread 27388, ...]
ntdll.dll!NtWaitForSingleObject()
KernelBase.dll!WaitForSingleObjectEx()
msvcp140.dll!_Thrd_join(_Thrd_t thr, int * code)
application.exe!std:: thread::join()
application.exe!`anonymous namespace'::ThreadManager::~ThreadManager()
application.exe!exitHandler()
ucrtbase.dll!(void)()
ucrtbase.dll!__crt_seh_guarded_call::operator()<<lambda_...>,(void) &,<lambda_...>>()
ucrtbase.dll!_execute_onexit_table()
ucrtbase.dll!(void)()
ucrtbase.dll!__crt_seh_guarded_call::operator()<<lambda_...>,(void) &,<lambda_...>>()
ucrtbase.dll!common_exit()
application.exe!__scrt_common_main_seh()
kernel32.dll!BaseThreadInitThunk()
ntdll.dll!RtlUserThreadStart()
Call stack of the managed thread (id 27388):
[Waiting on lock owned by Thread 126036, ...]
ntdll.dll!NtWaitForAlertByThreadId()
ntdll.dll!RtlpWaitOnAddressWithTimeout()
ntdll.dll!RtlpWaitOnAddress()
ntdll.dll!RtlpWaitOnCriticalSection()
ntdll.dll!RtlpEnterCriticalSectionContended()
ntdll.dll!RtlEnterCriticalSection()
ucrtbase.dll!__crt_seh_guarded_call::operator()<<lambda_...>,(void) &,<lambda_...>>()
ucrtbase.dll!_register_onexit_function()
opencv_core451.dll!_onexit(int(*)() function)
opencv_core451.dll!atexit(void(*)() function)
opencv_core451.dll!cv::details::getTlsAbstraction_() Line 1405
opencv_core451.dll!cv::details::getTlsAbstraction() Line 1410
opencv_core451.dll!cv::details::TlsStorage::releaseThread(void * tlsValue) Line 1531
opencv_core451.dll!DllMain(HINSTANCE__ * __formal, unsigned long fdwReason, void * lpReserved) Line 1824
opencv_core451.dll!dllmain_dispatch(HINSTANCE__ * const instance, const unsigned long reason, void * const reserved) Line 281
ntdll.dll!LdrpCallInitRoutine()
ntdll.dll!LdrShutdownThread()
ntdll.dll!RtlExitUserThread()
KernelBase.dll!FreeLibraryAndExitThread()
ucrtbase.dll!common_end_thread()
ucrtbase.dll!thread_start<unsigned int (__cdecl*)(void *),1>()
kernel32.dll!BaseThreadInitThunk()
ntdll.dll!RtlUserThreadStart()
The problem is OpenCV using cv::details::getTlsAbstraction_() when a thread exits, but this code refers to a singleton, and the compiler auto-creates an onexit handler for it, as shows the comment in the OpenCV code:
static TlsAbstraction& getTlsAbstraction_()
{
static TlsAbstraction g_tls; // disposed in atexit() handlers (required for unregistering our callbacks)
return g_tls;
}This fails if the application is already exiting and looping through the various exit handlers inside the critical section. So the main thread waits for the other thread to exit, while the other thread waits for the main thread to leave the critical section for the exit handlers.
I'd also be happy with a workaround - as I do not see a general solution for the issue.
Note: Sure I may ask the thread handler to terminate all threads before leaving main(), but this will not cover the cases where some other (maybe third-party) code calls exit()...