core(tls): unblock TlsAbstraction destructor call

- required to unregister callbacks from system
This commit is contained in:
Alexander Alekhin 2019-12-04 06:18:36 +00:00
parent eb44e0a556
commit 818585fd12

View File

@ -1408,16 +1408,44 @@ namespace details {
#endif
#endif
template <class T>
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<TlsAbstraction>
{
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<TlsAbstraction>::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<TlsAbstraction>::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<void*>& 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