mirror of
https://github.com/opencv/opencv.git
synced 2026-07-29 15:23:05 +04:00
core(tls): implement releasing of TLS on thread termination
- move TLS & instrumentation code out of core/utility.hpp - (*) TLSData lost .gather() method (to dispose thread data on thread termination) - use TLSDataAccumulator for reliable collecting of thread data - prefer using of .detachData() + .cleanupDetachedData() instead of .gather() method (*) API is broken: replace TLSData => TLSDataAccumulator if gather required (objects disposal on threads termination is not available in accumulator mode)
This commit is contained in:
+113
-57
@@ -50,6 +50,9 @@
|
||||
|
||||
#include <opencv2/core/utils/logger.hpp>
|
||||
|
||||
#include <opencv2/core/utils/tls.hpp>
|
||||
#include <opencv2/core/utils/instrumentation.hpp>
|
||||
|
||||
namespace cv {
|
||||
|
||||
static Mutex* __initialization_mutex = NULL;
|
||||
@@ -1375,6 +1378,8 @@ bool Mutex::trylock() { return impl->trylock(); }
|
||||
|
||||
//////////////////////////////// thread-local storage ////////////////////////////////
|
||||
|
||||
namespace details {
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable:4505) // unreferenced local function has been removed
|
||||
@@ -1436,9 +1441,10 @@ void TlsAbstraction::SetData(void *pData)
|
||||
}
|
||||
#endif
|
||||
#else // _WIN32
|
||||
static void opencv_tls_destructor(void* pData);
|
||||
TlsAbstraction::TlsAbstraction()
|
||||
{
|
||||
CV_Assert(pthread_key_create(&tlsKey, NULL) == 0);
|
||||
CV_Assert(pthread_key_create(&tlsKey, opencv_tls_destructor) == 0);
|
||||
}
|
||||
TlsAbstraction::~TlsAbstraction()
|
||||
{
|
||||
@@ -1479,42 +1485,46 @@ public:
|
||||
}
|
||||
~TlsStorage()
|
||||
{
|
||||
for(size_t i = 0; i < threads.size(); i++)
|
||||
{
|
||||
if(threads[i])
|
||||
{
|
||||
/* Current architecture doesn't allow proper global objects release, so this check can cause crashes
|
||||
|
||||
// Check if all slots were properly cleared
|
||||
for(size_t j = 0; j < threads[i]->slots.size(); j++)
|
||||
{
|
||||
CV_Assert(threads[i]->slots[j] == 0);
|
||||
}
|
||||
*/
|
||||
delete threads[i];
|
||||
}
|
||||
}
|
||||
threads.clear();
|
||||
// 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");
|
||||
}
|
||||
|
||||
void releaseThread()
|
||||
void releaseThread(void* tlsValue = NULL)
|
||||
{
|
||||
ThreadData *pTD = tlsValue == NULL ? (ThreadData*)tls.GetData() : (ThreadData*)tlsValue;
|
||||
if (pTD == NULL)
|
||||
return; // no OpenCV TLS data for this thread
|
||||
AutoLock guard(mtxGlobalAccess);
|
||||
ThreadData *pTD = (ThreadData*)tls.GetData();
|
||||
for(size_t i = 0; i < threads.size(); i++)
|
||||
for (size_t i = 0; i < threads.size(); i++)
|
||||
{
|
||||
if(pTD == threads[i])
|
||||
if (pTD == threads[i])
|
||||
{
|
||||
threads[i] = 0;
|
||||
break;
|
||||
threads[i] = NULL;
|
||||
if (tlsValue == NULL)
|
||||
tls.SetData(0);
|
||||
std::vector<void*>& thread_slots = pTD->slots;
|
||||
for (size_t slotIdx = 0; slotIdx < thread_slots.size(); slotIdx++)
|
||||
{
|
||||
void* pData = thread_slots[slotIdx];
|
||||
thread_slots[slotIdx] = NULL;
|
||||
if (!pData)
|
||||
continue;
|
||||
TLSDataContainer* container = tlsSlots[slotIdx].container;
|
||||
if (container)
|
||||
container->deleteDataInstance(pData);
|
||||
else
|
||||
CV_LOG_ERROR(NULL, "TLS: container for slotIdx=" << slotIdx << " is NULL. Can't release thread data");
|
||||
}
|
||||
delete pTD;
|
||||
return;
|
||||
}
|
||||
}
|
||||
tls.SetData(0);
|
||||
delete pTD;
|
||||
CV_LOG_WARNING(NULL, "TLS: Can't release thread TLS data (unknown pointer or data race): " << (void*)pTD);
|
||||
}
|
||||
|
||||
// Reserve TLS storage index
|
||||
size_t reserveSlot()
|
||||
size_t reserveSlot(TLSDataContainer* container)
|
||||
{
|
||||
AutoLock guard(mtxGlobalAccess);
|
||||
CV_Assert(tlsSlotsSize == tlsSlots.size());
|
||||
@@ -1522,15 +1532,15 @@ public:
|
||||
// Find unused slots
|
||||
for(size_t slot = 0; slot < tlsSlotsSize; slot++)
|
||||
{
|
||||
if(!tlsSlots[slot])
|
||||
if (tlsSlots[slot].container == NULL)
|
||||
{
|
||||
tlsSlots[slot] = 1;
|
||||
tlsSlots[slot].container = container;
|
||||
return slot;
|
||||
}
|
||||
}
|
||||
|
||||
// Create new slot
|
||||
tlsSlots.push_back(1); tlsSlotsSize++;
|
||||
tlsSlots.push_back(TlsSlotInfo(container)); tlsSlotsSize++;
|
||||
return tlsSlotsSize - 1;
|
||||
}
|
||||
|
||||
@@ -1555,7 +1565,9 @@ public:
|
||||
}
|
||||
|
||||
if (!keepSlot)
|
||||
tlsSlots[slotIdx] = 0;
|
||||
{
|
||||
tlsSlots[slotIdx].container = NULL; // mark slot as free (see reserveSlot() implementation)
|
||||
}
|
||||
}
|
||||
|
||||
// Get data by TLS storage index
|
||||
@@ -1604,8 +1616,26 @@ public:
|
||||
tls.SetData((void*)threadData);
|
||||
{
|
||||
AutoLock guard(mtxGlobalAccess);
|
||||
threadData->idx = threads.size();
|
||||
threads.push_back(threadData);
|
||||
|
||||
bool found = false;
|
||||
// Find unused slots
|
||||
for(size_t slot = 0; slot < threads.size(); slot++)
|
||||
{
|
||||
if (threads[slot] == NULL)
|
||||
{
|
||||
threadData->idx = (int)slot;
|
||||
threads[slot] = threadData;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
// Create new slot
|
||||
threadData->idx = threads.size();
|
||||
threads.push_back(threadData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1622,8 +1652,14 @@ private:
|
||||
|
||||
Mutex mtxGlobalAccess; // Shared objects operation guard
|
||||
size_t tlsSlotsSize; // equal to tlsSlots.size() in synchronized sections
|
||||
// without synchronization this counter doesn't desrease - it is used for slotIdx sanity checks
|
||||
std::vector<int> tlsSlots; // TLS keys state
|
||||
// without synchronization this counter doesn't decrease - it is used for slotIdx sanity checks
|
||||
|
||||
struct TlsSlotInfo
|
||||
{
|
||||
TlsSlotInfo(TLSDataContainer* _container) : container(_container) {}
|
||||
TLSDataContainer* container; // attached container (to dispose data of terminated threads)
|
||||
};
|
||||
std::vector<struct TlsSlotInfo> tlsSlots; // TLS keys state
|
||||
std::vector<ThreadData*> threads; // Array for all allocated data. Thread data pointers are placed here to allow data cleanup
|
||||
};
|
||||
|
||||
@@ -1633,9 +1669,19 @@ static TlsStorage &getTlsStorage()
|
||||
CV_SINGLETON_LAZY_INIT_REF(TlsStorage, new TlsStorage())
|
||||
}
|
||||
|
||||
#ifndef _WIN32 // pthread key destructor
|
||||
static void opencv_tls_destructor(void* pData)
|
||||
{
|
||||
getTlsStorage().releaseThread(pData);
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace details
|
||||
using namespace details;
|
||||
|
||||
TLSDataContainer::TLSDataContainer()
|
||||
{
|
||||
key_ = (int)getTlsStorage().reserveSlot(); // Reserve key from TLS storage
|
||||
key_ = (int)getTlsStorage().reserveSlot(this); // Reserve key from TLS storage
|
||||
}
|
||||
|
||||
TLSDataContainer::~TLSDataContainer()
|
||||
@@ -1648,11 +1694,17 @@ void TLSDataContainer::gatherData(std::vector<void*> &data) const
|
||||
getTlsStorage().gather(key_, data);
|
||||
}
|
||||
|
||||
void TLSDataContainer::detachData(std::vector<void*> &data)
|
||||
{
|
||||
getTlsStorage().releaseSlot(key_, data, true);
|
||||
}
|
||||
|
||||
void TLSDataContainer::release()
|
||||
{
|
||||
std::vector<void*> data;
|
||||
data.reserve(32);
|
||||
getTlsStorage().releaseSlot(key_, data); // Release key and get stored data for proper destruction
|
||||
if (key_ == -1)
|
||||
return; // already released
|
||||
std::vector<void*> data; data.reserve(32);
|
||||
getTlsStorage().releaseSlot(key_, data, false); // Release key and get stored data for proper destruction
|
||||
key_ = -1;
|
||||
for(size_t i = 0; i < data.size(); i++) // Delete all associated data
|
||||
deleteDataInstance(data[i]);
|
||||
@@ -1660,8 +1712,7 @@ void TLSDataContainer::release()
|
||||
|
||||
void TLSDataContainer::cleanup()
|
||||
{
|
||||
std::vector<void*> data;
|
||||
data.reserve(32);
|
||||
std::vector<void*> data; data.reserve(32);
|
||||
getTlsStorage().releaseSlot(key_, data, true); // Extract stored data with removal from TLS tables
|
||||
for(size_t i = 0; i < data.size(); i++) // Delete all associated data
|
||||
deleteDataInstance(data[i]);
|
||||
@@ -1680,11 +1731,16 @@ void* TLSDataContainer::getData() const
|
||||
return pData;
|
||||
}
|
||||
|
||||
TLSData<CoreTLSData>& getCoreTlsData()
|
||||
static TLSData<CoreTLSData>& getCoreTlsDataTLS()
|
||||
{
|
||||
CV_SINGLETON_LAZY_INIT_REF(TLSData<CoreTLSData>, new TLSData<CoreTLSData>())
|
||||
}
|
||||
|
||||
CoreTLSData& getCoreTlsData()
|
||||
{
|
||||
return getCoreTlsDataTLS().getRef();
|
||||
}
|
||||
|
||||
#if defined CVAPI_EXPORTS && defined _WIN32 && !defined WINCE
|
||||
#ifdef WINRT
|
||||
#pragma warning(disable:4447) // Disable warning 'main' signature found without threading model
|
||||
@@ -2338,12 +2394,12 @@ String getIppVersion()
|
||||
bool useIPP()
|
||||
{
|
||||
#ifdef HAVE_IPP
|
||||
CoreTLSData* data = getCoreTlsData().get();
|
||||
if(data->useIPP < 0)
|
||||
CoreTLSData& data = getCoreTlsData();
|
||||
if (data.useIPP < 0)
|
||||
{
|
||||
data->useIPP = getIPPSingleton().useIPP;
|
||||
data.useIPP = getIPPSingleton().useIPP;
|
||||
}
|
||||
return (data->useIPP > 0);
|
||||
return (data.useIPP > 0);
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
@@ -2351,24 +2407,24 @@ bool useIPP()
|
||||
|
||||
void setUseIPP(bool flag)
|
||||
{
|
||||
CoreTLSData* data = getCoreTlsData().get();
|
||||
CoreTLSData& data = getCoreTlsData();
|
||||
#ifdef HAVE_IPP
|
||||
data->useIPP = (getIPPSingleton().useIPP)?flag:false;
|
||||
data.useIPP = (getIPPSingleton().useIPP)?flag:false;
|
||||
#else
|
||||
CV_UNUSED(flag);
|
||||
data->useIPP = false;
|
||||
data.useIPP = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool useIPP_NotExact()
|
||||
{
|
||||
#ifdef HAVE_IPP
|
||||
CoreTLSData* data = getCoreTlsData().get();
|
||||
if(data->useIPP_NE < 0)
|
||||
CoreTLSData& data = getCoreTlsData();
|
||||
if (data.useIPP_NE < 0)
|
||||
{
|
||||
data->useIPP_NE = getIPPSingleton().useIPP_NE;
|
||||
data.useIPP_NE = getIPPSingleton().useIPP_NE;
|
||||
}
|
||||
return (data->useIPP_NE > 0);
|
||||
return (data.useIPP_NE > 0);
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
@@ -2376,12 +2432,12 @@ bool useIPP_NotExact()
|
||||
|
||||
void setUseIPP_NotExact(bool flag)
|
||||
{
|
||||
CoreTLSData* data = getCoreTlsData().get();
|
||||
CoreTLSData& data = getCoreTlsData();
|
||||
#ifdef HAVE_IPP
|
||||
data->useIPP_NE = flag;
|
||||
data.useIPP_NE = flag;
|
||||
#else
|
||||
CV_UNUSED(flag);
|
||||
data->useIPP_NE = false;
|
||||
data.useIPP_NE = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -2407,7 +2463,7 @@ namespace tegra {
|
||||
|
||||
bool useTegra()
|
||||
{
|
||||
cv::CoreTLSData* data = cv::getCoreTlsData().get();
|
||||
cv::CoreTLSData* data = cv::getCoreTlsData();
|
||||
|
||||
if (data->useTegra < 0)
|
||||
{
|
||||
@@ -2423,7 +2479,7 @@ bool useTegra()
|
||||
|
||||
void setUseTegra(bool flag)
|
||||
{
|
||||
cv::CoreTLSData* data = cv::getCoreTlsData().get();
|
||||
cv::CoreTLSData* data = cv::getCoreTlsData();
|
||||
data->useTegra = flag;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user