From 818585fd12287dab9168c1406d6092152aa8a0e2 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Wed, 4 Dec 2019 06:18:36 +0000 Subject: [PATCH] core(tls): unblock TlsAbstraction destructor call - required to unregister callbacks from system --- modules/core/src/system.cpp | 112 +++++++++++++++++++++++++++++------- 1 file changed, 92 insertions(+), 20 deletions(-) diff --git a/modules/core/src/system.cpp b/modules/core/src/system.cpp index ed86bbb46c..699fecccfc 100644 --- a/modules/core/src/system.cpp +++ b/modules/core/src/system.cpp @@ -1408,16 +1408,44 @@ 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 +class TlsAbstraction : public DisposedSingletonMark { public: TlsAbstraction(); ~TlsAbstraction(); - void* GetData() const; - void SetData(void *pData); + void* getData() const + { + 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); + } private: + void* getData_() const; + void setData_(void *pData); + #ifdef _WIN32 #ifndef WINRT DWORD tlsKey; @@ -1427,16 +1455,40 @@ private: #endif }; +template<> bool DisposedSingletonMark::mark = false; + +static TlsAbstraction& getTlsAbstraction_() +{ + static TlsAbstraction g_tls; // disposed in atexit() handlers (required for unregistering our callbacks) + return g_tls; +} +static TlsAbstraction* getTlsAbstraction() +{ +#ifdef CV_CXX11 + static TlsAbstraction* instance = &getTlsAbstraction_(); +#else + static TlsAbstraction* volatile instance = NULL; + if (instance == NULL) + { + cv::AutoLock lock(cv::getInitializationMutex()); + if (instance == NULL) + instance = &getTlsAbstraction_(); + } +#endif + return DisposedSingletonMark::isDisposed() ? NULL : instance; +} + + #ifdef _WIN32 #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::getData_() const { return tlsData; } -void TlsAbstraction::SetData(void *pData) +void TlsAbstraction::setData_(void *pData) { tlsData = pData; } @@ -1460,8 +1512,9 @@ TlsAbstraction::~TlsAbstraction() #else // CV_USE_FLS FlsFree(tlsKey); #endif // CV_USE_FLS + tlsKey = TLS_OUT_OF_INDEXES; } -void* TlsAbstraction::GetData() const +void* TlsAbstraction::getData_() const { #ifndef CV_USE_FLS return TlsGetValue(tlsKey); @@ -1469,7 +1522,7 @@ void* TlsAbstraction::GetData() const return FlsGetValue(tlsKey); #endif // CV_USE_FLS } -void TlsAbstraction::SetData(void *pData) +void TlsAbstraction::setData_(void *pData) { #ifndef CV_USE_FLS CV_Assert(TlsSetValue(tlsKey, pData) == TRUE); @@ -1486,13 +1539,18 @@ TlsAbstraction::TlsAbstraction() } TlsAbstraction::~TlsAbstraction() { - CV_Assert(pthread_key_delete(tlsKey) == 0); + if (pthread_key_delete(tlsKey) != 0) + { + // Don't use logging here + fprintf(stderr, "OpenCV ERROR: TlsAbstraction::~TlsAbstraction(): pthread_key_delete() call failed\n"); + fflush(stderr); + } } -void* TlsAbstraction::GetData() const +void* TlsAbstraction::getData_() const { return pthread_getspecific(tlsKey); } -void TlsAbstraction::SetData(void *pData) +void TlsAbstraction::setData_(void *pData) { CV_Assert(pthread_setspecific(tlsKey, pData) == 0); } @@ -1525,12 +1583,17 @@ public: { // TlsStorage object should not be released // There is no reliable way to avoid problems caused by static initialization order fiasco - CV_LOG_FATAL(NULL, "TlsStorage::~TlsStorage() call is not expected"); + // Don't use logging here + fprintf(stderr, "OpenCV FATAL: TlsStorage::~TlsStorage() call is not expected\n"); + fflush(stderr); } void releaseThread(void* tlsValue = NULL) { - ThreadData *pTD = tlsValue == NULL ? (ThreadData*)tls.GetData() : (ThreadData*)tlsValue; + TlsAbstraction* tls = getTlsAbstraction(); + if (NULL == tls) + return; // TLS signleton is not available (terminated) + ThreadData *pTD = tlsValue == NULL ? (ThreadData*)tls->getData() : (ThreadData*)tlsValue; if (pTD == NULL) return; // no OpenCV TLS data for this thread AutoLock guard(mtxGlobalAccess); @@ -1540,7 +1603,7 @@ public: { threads[i] = NULL; if (tlsValue == NULL) - tls.SetData(0); + tls->setData(0); std::vector& thread_slots = pTD->slots; for (size_t slotIdx = 0; slotIdx < thread_slots.size(); slotIdx++) { @@ -1552,13 +1615,16 @@ public: if (container) container->deleteDataInstance(pData); else - CV_LOG_ERROR(NULL, "TLS: container for slotIdx=" << slotIdx << " is NULL. Can't release thread data"); + { + fprintf(stderr, "OpenCV ERROR: TLS: container for slotIdx=%d is NULL. Can't release thread data\n", (int)slotIdx); + fflush(stderr); + } } delete pTD; return; } } - CV_LOG_WARNING(NULL, "TLS: Can't release thread TLS data (unknown pointer or data race): " << (void*)pTD); + fprintf(stderr, "OpenCV WARNING: TLS: Can't release thread TLS data (unknown pointer or data race): %p\n", (void*)pTD); fflush(stderr); } // Reserve TLS storage index @@ -1615,7 +1681,11 @@ public: CV_Assert(tlsSlotsSize > slotIdx); #endif - ThreadData* threadData = (ThreadData*)tls.GetData(); + TlsAbstraction* tls = getTlsAbstraction(); + if (NULL == tls) + return NULL; // TLS signleton is not available (terminated) + + ThreadData* threadData = (ThreadData*)tls->getData(); if(threadData && threadData->slots.size() > slotIdx) return threadData->slots[slotIdx]; @@ -1647,11 +1717,15 @@ public: CV_Assert(tlsSlotsSize > slotIdx); #endif - ThreadData* threadData = (ThreadData*)tls.GetData(); + TlsAbstraction* tls = getTlsAbstraction(); + if (NULL == tls) + return; // TLS signleton is not available (terminated) + + ThreadData* threadData = (ThreadData*)tls->getData(); if(!threadData) { threadData = new ThreadData; - tls.SetData((void*)threadData); + tls->setData((void*)threadData); { AutoLock guard(mtxGlobalAccess); @@ -1686,8 +1760,6 @@ public: } private: - TlsAbstraction tls; // TLS abstraction layer instance - Mutex mtxGlobalAccess; // Shared objects operation guard size_t tlsSlotsSize; // equal to tlsSlots.size() in synchronized sections // without synchronization this counter doesn't decrease - it is used for slotIdx sanity checks