Skip to content

Deadlock on application exit in multi-threaded environment #19875

@rwiesenfarth

Description

@rwiesenfarth
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()...

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions