From 4985311d46f8e073646de3d2abff9ff9a8c7dd05 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Fri, 8 Oct 2021 04:36:58 +0300 Subject: [PATCH] core(tls): avoid destruction of TlsAbstraction singleton --- modules/core/src/system.cpp | 135 ++++++++++++++++++++++-------------- 1 file changed, 83 insertions(+), 52 deletions(-) diff --git a/modules/core/src/system.cpp b/modules/core/src/system.cpp index e2284b0415..27142a4034 100644 --- a/modules/core/src/system.cpp +++ b/modules/core/src/system.cpp @@ -53,6 +53,18 @@ #include #include +#ifndef OPENCV_WITH_THREAD_SANITIZER + #if defined(__clang__) && defined(__has_feature) + #if __has_feature(thread_sanitizer) + #define OPENCV_WITH_THREAD_SANITIZER 1 + #include // assume C++11 + #endif + #endif +#endif +#ifndef OPENCV_WITH_THREAD_SANITIZER + #define OPENCV_WITH_THREAD_SANITIZER 0 +#endif + namespace cv { static void _initSystem() @@ -1426,74 +1438,75 @@ namespace details { #endif #endif -template -class DisposedSingletonMark -{ -private: - static bool mark; -protected: - DisposedSingletonMark() {} - ~DisposedSingletonMark() - { - mark = true; - } -public: - static bool isDisposed() { return mark; } -}; - // TLS platform abstraction layer -class TlsAbstraction : public DisposedSingletonMark +class TlsAbstraction { public: TlsAbstraction(); - ~TlsAbstraction(); - void* getData() const + ~TlsAbstraction() { - if (isDisposed()) // guard: static initialization order fiasco - return NULL; - return getData_(); - } - void setData(void *pData) - { - if (isDisposed()) // guard: static initialization order fiasco - return; - return setData_(pData); + // TlsAbstraction singleton should not be released + // There is no reliable way to avoid problems caused by static initialization order fiasco + // NB: Do NOT use logging here + fprintf(stderr, "OpenCV FATAL: TlsAbstraction::~TlsAbstraction() call is not expected\n"); + fflush(stderr); } + void* getData() const; + void setData(void *pData); + + void releaseSystemResources(); + private: - void* getData_() const; - void setData_(void *pData); #ifdef _WIN32 #ifndef WINRT DWORD tlsKey; + bool disposed; #endif #else // _WIN32 pthread_key_t tlsKey; +#if OPENCV_WITH_THREAD_SANITIZER + std::atomic disposed; +#else + bool disposed; +#endif #endif }; -template<> bool DisposedSingletonMark::mark = false; - -static TlsAbstraction& getTlsAbstraction_() +class TlsAbstractionReleaseGuard { - static TlsAbstraction g_tls; // disposed in atexit() handlers (required for unregistering our callbacks) - return g_tls; -} + TlsAbstraction& tls_; +public: + TlsAbstractionReleaseGuard(TlsAbstraction& tls) : tls_(tls) + { + /* nothing */ + } + ~TlsAbstractionReleaseGuard() + { + tls_.releaseSystemResources(); + } +}; + +// TODO use reference static TlsAbstraction* getTlsAbstraction() { #ifdef CV_CXX11 - static TlsAbstraction* instance = &getTlsAbstraction_(); + static TlsAbstraction *g_tls = new TlsAbstraction(); // memory leak is intended here to avoid disposing of TLS container + static TlsAbstractionReleaseGuard g_tlsReleaseGuard(*g_tls); #else - static TlsAbstraction* volatile instance = NULL; - if (instance == NULL) + static TlsAbstraction* volatile g_tls = NULL; + if (g_tls == NULL) { cv::AutoLock lock(cv::getInitializationMutex()); - if (instance == NULL) - instance = &getTlsAbstraction_(); + if (g_tls == NULL) + { + g_tls = new TlsAbstraction(); + static TlsAbstractionReleaseGuard g_tlsReleaseGuard(*g_tls); + } } #endif - return DisposedSingletonMark::isDisposed() ? NULL : instance; + return g_tls; } @@ -1501,12 +1514,15 @@ static TlsAbstraction* getTlsAbstraction() #ifdef WINRT static __declspec( thread ) void* tlsData = NULL; // using C++11 thread attribute for local thread data TlsAbstraction::TlsAbstraction() {} -TlsAbstraction::~TlsAbstraction() {} -void* TlsAbstraction::getData_() const +void TlsAbstraction::releaseSystemResources() +{ + cv::__termination = true; // DllMain is missing in static builds +} +void* TlsAbstraction::getData() const { return tlsData; } -void TlsAbstraction::setData_(void *pData) +void TlsAbstraction::setData(void *pData) { tlsData = pData; } @@ -1515,6 +1531,7 @@ void TlsAbstraction::setData_(void *pData) static void NTAPI opencv_fls_destructor(void* pData); #endif // CV_USE_FLS TlsAbstraction::TlsAbstraction() + : disposed(false) { #ifndef CV_USE_FLS tlsKey = TlsAlloc(); @@ -1523,8 +1540,10 @@ TlsAbstraction::TlsAbstraction() #endif // CV_USE_FLS CV_Assert(tlsKey != TLS_OUT_OF_INDEXES); } -TlsAbstraction::~TlsAbstraction() +void TlsAbstraction::releaseSystemResources() { + cv::__termination = true; // DllMain is missing in static builds + disposed = true; #ifndef CV_USE_FLS TlsFree(tlsKey); #else // CV_USE_FLS @@ -1532,16 +1551,20 @@ TlsAbstraction::~TlsAbstraction() #endif // CV_USE_FLS tlsKey = TLS_OUT_OF_INDEXES; } -void* TlsAbstraction::getData_() const +void* TlsAbstraction::getData() const { + if (disposed) + return NULL; #ifndef CV_USE_FLS return TlsGetValue(tlsKey); #else // CV_USE_FLS return FlsGetValue(tlsKey); #endif // CV_USE_FLS } -void TlsAbstraction::setData_(void *pData) +void TlsAbstraction::setData(void *pData) { + if (disposed) + return; // no-op #ifndef CV_USE_FLS CV_Assert(TlsSetValue(tlsKey, pData) == TRUE); #else // CV_USE_FLS @@ -1552,11 +1575,14 @@ void TlsAbstraction::setData_(void *pData) #else // _WIN32 static void opencv_tls_destructor(void* pData); TlsAbstraction::TlsAbstraction() + : disposed(false) { CV_Assert(pthread_key_create(&tlsKey, opencv_tls_destructor) == 0); } -TlsAbstraction::~TlsAbstraction() +void TlsAbstraction::releaseSystemResources() { + cv::__termination = true; // DllMain is missing in static builds + disposed = true; if (pthread_key_delete(tlsKey) != 0) { // Don't use logging here @@ -1564,12 +1590,16 @@ TlsAbstraction::~TlsAbstraction() fflush(stderr); } } -void* TlsAbstraction::getData_() const +void* TlsAbstraction::getData() const { + if (disposed) + return NULL; return pthread_getspecific(tlsKey); } -void TlsAbstraction::setData_(void *pData) +void TlsAbstraction::setData(void *pData) { + if (disposed) + return; // no-op CV_Assert(pthread_setspecific(tlsKey, pData) == 0); } #endif @@ -1597,6 +1627,7 @@ public: TlsStorage() : tlsSlotsSize(0) { + (void)getTlsAbstraction(); // ensure singeton initialization (for correct order of atexit calls) tlsSlots.reserve(32); threads.reserve(32); g_isTlsStorageInitialized = true; @@ -1834,11 +1865,11 @@ static void WINAPI opencv_fls_destructor(void* pData) #endif // CV_USE_FLS #endif // _WIN32 -static TlsAbstraction* const g_force_initialization_of_TlsAbstraction +static TlsStorage* const g_force_initialization_of_TlsStorage #if defined __GNUC__ __attribute__((unused)) #endif - = getTlsAbstraction(); + = &getTlsStorage(); } // namespace details using namespace details;