|
|
|
@ -936,241 +936,280 @@ bool Mutex::trylock() { return impl->trylock(); } |
|
|
|
|
|
|
|
|
|
//////////////////////////////// thread-local storage ////////////////////////////////
|
|
|
|
|
|
|
|
|
|
class TLSStorage |
|
|
|
|
#ifdef WIN32 |
|
|
|
|
#ifdef _MSC_VER |
|
|
|
|
#pragma warning(disable:4505) // unreferenced local function has been removed
|
|
|
|
|
#endif |
|
|
|
|
#ifndef TLS_OUT_OF_INDEXES |
|
|
|
|
#define TLS_OUT_OF_INDEXES ((DWORD)0xFFFFFFFF) |
|
|
|
|
#endif |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
// TLS platform abstraction layer
|
|
|
|
|
class TlsAbstraction |
|
|
|
|
{ |
|
|
|
|
std::vector<void*> tlsData_; |
|
|
|
|
public: |
|
|
|
|
TLSStorage() { tlsData_.reserve(16); } |
|
|
|
|
~TLSStorage(); |
|
|
|
|
inline void* getData(int key) const |
|
|
|
|
{ |
|
|
|
|
CV_DbgAssert(key >= 0); |
|
|
|
|
return (key < (int)tlsData_.size()) ? tlsData_[key] : NULL; |
|
|
|
|
} |
|
|
|
|
inline void setData(int key, void* data) |
|
|
|
|
{ |
|
|
|
|
CV_DbgAssert(key >= 0); |
|
|
|
|
if (key >= (int)tlsData_.size()) |
|
|
|
|
{ |
|
|
|
|
tlsData_.resize(key + 1, NULL); |
|
|
|
|
} |
|
|
|
|
tlsData_[key] = data; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
inline static TLSStorage* get(); |
|
|
|
|
}; |
|
|
|
|
TlsAbstraction(); |
|
|
|
|
~TlsAbstraction(); |
|
|
|
|
void* GetData() const; |
|
|
|
|
void SetData(void *pData); |
|
|
|
|
|
|
|
|
|
private: |
|
|
|
|
#ifdef WIN32 |
|
|
|
|
#ifdef _MSC_VER |
|
|
|
|
#pragma warning(disable:4505) // unreferenced local function has been removed
|
|
|
|
|
#ifndef WINRT |
|
|
|
|
DWORD tlsKey; |
|
|
|
|
#endif |
|
|
|
|
#else // WIN32
|
|
|
|
|
pthread_key_t tlsKey; |
|
|
|
|
#endif |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
#ifdef WIN32 |
|
|
|
|
#ifdef WINRT |
|
|
|
|
// using C++11 thread attribute for local thread data
|
|
|
|
|
static __declspec( thread ) TLSStorage* g_tlsdata = NULL; |
|
|
|
|
static __declspec( thread ) void* tlsData = NULL; // using C++11 thread attribute for local thread data
|
|
|
|
|
TlsAbstraction::TlsAbstraction() {} |
|
|
|
|
TlsAbstraction::~TlsAbstraction() {} |
|
|
|
|
void* TlsAbstraction::GetData() const |
|
|
|
|
{ |
|
|
|
|
return tlsData; |
|
|
|
|
} |
|
|
|
|
void TlsAbstraction::SetData(void *pData) |
|
|
|
|
{ |
|
|
|
|
tlsData = pData; |
|
|
|
|
} |
|
|
|
|
#else //WINRT
|
|
|
|
|
TlsAbstraction::TlsAbstraction() |
|
|
|
|
{ |
|
|
|
|
tlsKey = TlsAlloc(); |
|
|
|
|
CV_Assert(tlsKey != TLS_OUT_OF_INDEXES); |
|
|
|
|
} |
|
|
|
|
TlsAbstraction::~TlsAbstraction() |
|
|
|
|
{ |
|
|
|
|
TlsFree(tlsKey); |
|
|
|
|
} |
|
|
|
|
void* TlsAbstraction::GetData() const |
|
|
|
|
{ |
|
|
|
|
return TlsGetValue(tlsKey); |
|
|
|
|
} |
|
|
|
|
void TlsAbstraction::SetData(void *pData) |
|
|
|
|
{ |
|
|
|
|
CV_Assert(TlsSetValue(tlsKey, pData) == TRUE); |
|
|
|
|
} |
|
|
|
|
#endif |
|
|
|
|
#else // WIN32
|
|
|
|
|
TlsAbstraction::TlsAbstraction() |
|
|
|
|
{ |
|
|
|
|
CV_Assert(pthread_key_create(&tlsKey, NULL) == 0); |
|
|
|
|
} |
|
|
|
|
TlsAbstraction::~TlsAbstraction() |
|
|
|
|
{ |
|
|
|
|
CV_Assert(pthread_key_delete(tlsKey) == 0); |
|
|
|
|
} |
|
|
|
|
void* TlsAbstraction::GetData() const |
|
|
|
|
{ |
|
|
|
|
return pthread_getspecific(tlsKey); |
|
|
|
|
} |
|
|
|
|
void TlsAbstraction::SetData(void *pData) |
|
|
|
|
{ |
|
|
|
|
CV_Assert(pthread_setspecific(tlsKey, pData) == 0); |
|
|
|
|
} |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
static void deleteThreadData() |
|
|
|
|
// Per-thread data structure
|
|
|
|
|
struct ThreadData |
|
|
|
|
{ |
|
|
|
|
ThreadData() |
|
|
|
|
{ |
|
|
|
|
if (g_tlsdata) |
|
|
|
|
{ |
|
|
|
|
delete g_tlsdata; |
|
|
|
|
g_tlsdata = NULL; |
|
|
|
|
} |
|
|
|
|
idx = 0; |
|
|
|
|
slots.reserve(32); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
inline TLSStorage* TLSStorage::get() |
|
|
|
|
{ |
|
|
|
|
if (!g_tlsdata) |
|
|
|
|
{ |
|
|
|
|
g_tlsdata = new TLSStorage; |
|
|
|
|
} |
|
|
|
|
return g_tlsdata; |
|
|
|
|
} |
|
|
|
|
#else |
|
|
|
|
#ifdef WINCE |
|
|
|
|
# define TLS_OUT_OF_INDEXES ((DWORD)0xFFFFFFFF) |
|
|
|
|
#endif |
|
|
|
|
static DWORD tlsKey = TLS_OUT_OF_INDEXES; |
|
|
|
|
std::vector<void*> slots; // Data array for a thread
|
|
|
|
|
size_t idx; // Thread index in TLS storage. This is not OS thread ID!
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
static void deleteThreadData() |
|
|
|
|
// Main TLS storage class
|
|
|
|
|
class TlsStorage |
|
|
|
|
{ |
|
|
|
|
public: |
|
|
|
|
TlsStorage() |
|
|
|
|
{ |
|
|
|
|
if(tlsKey != TLS_OUT_OF_INDEXES) |
|
|
|
|
{ |
|
|
|
|
delete (TLSStorage*)TlsGetValue(tlsKey); |
|
|
|
|
TlsSetValue(tlsKey, NULL); |
|
|
|
|
} |
|
|
|
|
tlsSlots = 0; |
|
|
|
|
threads.reserve(32); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
inline TLSStorage* TLSStorage::get() |
|
|
|
|
~TlsStorage() |
|
|
|
|
{ |
|
|
|
|
if (tlsKey == TLS_OUT_OF_INDEXES) |
|
|
|
|
for(size_t i = 0; i < threads.size(); i++) |
|
|
|
|
{ |
|
|
|
|
tlsKey = TlsAlloc(); |
|
|
|
|
CV_Assert(tlsKey != TLS_OUT_OF_INDEXES); |
|
|
|
|
} |
|
|
|
|
TLSStorage* d = (TLSStorage*)TlsGetValue(tlsKey); |
|
|
|
|
if (!d) |
|
|
|
|
{ |
|
|
|
|
d = new TLSStorage; |
|
|
|
|
TlsSetValue(tlsKey, d); |
|
|
|
|
if(threads[i]) |
|
|
|
|
{ |
|
|
|
|
/* Current architecture doesn't allow proper global objects relase, 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]; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return d; |
|
|
|
|
threads.clear(); |
|
|
|
|
} |
|
|
|
|
#endif //WINRT
|
|
|
|
|
|
|
|
|
|
#if defined CVAPI_EXPORTS && defined WIN32 && !defined WINCE |
|
|
|
|
#ifdef WINRT |
|
|
|
|
#pragma warning(disable:4447) // Disable warning 'main' signature found without threading model
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
extern "C" |
|
|
|
|
BOOL WINAPI DllMain(HINSTANCE, DWORD fdwReason, LPVOID lpReserved) |
|
|
|
|
{ |
|
|
|
|
if (fdwReason == DLL_THREAD_DETACH || fdwReason == DLL_PROCESS_DETACH) |
|
|
|
|
void releaseThread() |
|
|
|
|
{ |
|
|
|
|
if (lpReserved != NULL) // called after ExitProcess() call
|
|
|
|
|
AutoLock guard(mtxGlobalAccess); |
|
|
|
|
ThreadData *pTD = (ThreadData*)tls.GetData(); |
|
|
|
|
for(size_t i = 0; i < threads.size(); i++) |
|
|
|
|
{ |
|
|
|
|
cv::__termination = true; |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
// Not allowed to free resources if lpReserved is non-null
|
|
|
|
|
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms682583.aspx
|
|
|
|
|
cv::deleteThreadAllocData(); |
|
|
|
|
cv::deleteThreadData(); |
|
|
|
|
if(pTD == threads[i]) |
|
|
|
|
{ |
|
|
|
|
threads[i] = 0; |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
tls.SetData(0); |
|
|
|
|
delete pTD; |
|
|
|
|
} |
|
|
|
|
return TRUE; |
|
|
|
|
} |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
#else |
|
|
|
|
static pthread_key_t tlsKey = 0; |
|
|
|
|
static pthread_once_t tlsKeyOnce = PTHREAD_ONCE_INIT; |
|
|
|
|
|
|
|
|
|
static void deleteTLSStorage(void* data) |
|
|
|
|
// Reserve TLS storage index
|
|
|
|
|
size_t reserveSlot() |
|
|
|
|
{ |
|
|
|
|
delete (TLSStorage*)data; |
|
|
|
|
AutoLock guard(mtxGlobalAccess); |
|
|
|
|
tlsSlots++; |
|
|
|
|
return (tlsSlots-1); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void makeKey() |
|
|
|
|
// Release TLS storage index and pass assosiated data to caller
|
|
|
|
|
void releaseSlot(size_t slotIdx, std::vector<void*> &dataVec) |
|
|
|
|
{ |
|
|
|
|
int errcode = pthread_key_create(&tlsKey, deleteTLSStorage); |
|
|
|
|
CV_Assert(errcode == 0); |
|
|
|
|
} |
|
|
|
|
AutoLock guard(mtxGlobalAccess); |
|
|
|
|
CV_Assert(tlsSlots > slotIdx); |
|
|
|
|
|
|
|
|
|
inline TLSStorage* TLSStorage::get() |
|
|
|
|
{ |
|
|
|
|
pthread_once(&tlsKeyOnce, makeKey); |
|
|
|
|
TLSStorage* d = (TLSStorage*)pthread_getspecific(tlsKey); |
|
|
|
|
if( !d ) |
|
|
|
|
for(size_t i = 0; i < threads.size(); i++) |
|
|
|
|
{ |
|
|
|
|
d = new TLSStorage; |
|
|
|
|
pthread_setspecific(tlsKey, d); |
|
|
|
|
if(threads[i]->slots[slotIdx]) |
|
|
|
|
{ |
|
|
|
|
dataVec.push_back(threads[i]->slots[slotIdx]); |
|
|
|
|
threads[i]->slots[slotIdx] = 0; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return d; |
|
|
|
|
// If we removing last element, decriment slots size to save space
|
|
|
|
|
if(tlsSlots-1 == slotIdx) |
|
|
|
|
tlsSlots--; |
|
|
|
|
} |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
class TLSContainerStorage |
|
|
|
|
{ |
|
|
|
|
cv::Mutex mutex_; |
|
|
|
|
std::vector<TLSDataContainer*> tlsContainers_; |
|
|
|
|
public: |
|
|
|
|
TLSContainerStorage() { } |
|
|
|
|
~TLSContainerStorage() |
|
|
|
|
// Get data by TLS storage index
|
|
|
|
|
void* getData(size_t slotIdx) const |
|
|
|
|
{ |
|
|
|
|
for (size_t i = 0; i < tlsContainers_.size(); i++) |
|
|
|
|
{ |
|
|
|
|
CV_DbgAssert(tlsContainers_[i] == NULL); // not all keys released
|
|
|
|
|
tlsContainers_[i] = NULL; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
CV_Assert(tlsSlots > slotIdx); |
|
|
|
|
|
|
|
|
|
int allocateKey(TLSDataContainer* pContainer) |
|
|
|
|
{ |
|
|
|
|
cv::AutoLock lock(mutex_); |
|
|
|
|
tlsContainers_.push_back(pContainer); |
|
|
|
|
return (int)tlsContainers_.size() - 1; |
|
|
|
|
} |
|
|
|
|
void releaseKey(int id, TLSDataContainer* pContainer) |
|
|
|
|
{ |
|
|
|
|
cv::AutoLock lock(mutex_); |
|
|
|
|
CV_Assert(tlsContainers_[id] == pContainer); |
|
|
|
|
tlsContainers_[id] = NULL; |
|
|
|
|
// currently, we don't go into thread's TLSData and release data for this key
|
|
|
|
|
ThreadData* threadData = (ThreadData*)tls.GetData(); |
|
|
|
|
if(threadData && threadData->slots.size() > slotIdx) |
|
|
|
|
return threadData->slots[slotIdx]; |
|
|
|
|
|
|
|
|
|
return NULL; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void destroyData(int key, void* data) |
|
|
|
|
// Set data to storage index
|
|
|
|
|
void setData(size_t slotIdx, void* pData) |
|
|
|
|
{ |
|
|
|
|
cv::AutoLock lock(mutex_); |
|
|
|
|
TLSDataContainer* k = tlsContainers_[key]; |
|
|
|
|
if (!k) |
|
|
|
|
return; |
|
|
|
|
try |
|
|
|
|
{ |
|
|
|
|
k->deleteDataInstance(data); |
|
|
|
|
} |
|
|
|
|
catch (...) |
|
|
|
|
CV_Assert(pData != NULL); |
|
|
|
|
|
|
|
|
|
ThreadData* threadData = (ThreadData*)tls.GetData(); |
|
|
|
|
if(!threadData) |
|
|
|
|
{ |
|
|
|
|
CV_DbgAssert(k == NULL); // Debug this!
|
|
|
|
|
threadData = new ThreadData; |
|
|
|
|
tls.SetData((void*)threadData); |
|
|
|
|
{ |
|
|
|
|
AutoLock guard(mtxGlobalAccess); |
|
|
|
|
threadData->idx = threads.size(); |
|
|
|
|
threads.push_back(threadData); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if(slotIdx >= threadData->slots.size()) |
|
|
|
|
threadData->slots.resize(slotIdx+1); |
|
|
|
|
threadData->slots[slotIdx] = pData; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private: |
|
|
|
|
TlsAbstraction tls; // TLS abstraction layer instance
|
|
|
|
|
|
|
|
|
|
Mutex mtxGlobalAccess; // Shared objects operation guard
|
|
|
|
|
size_t tlsSlots; // TLS storage counter
|
|
|
|
|
std::vector<ThreadData*> threads; // Array for all allocated data. Thread data pointers are placed here to allow data cleanup
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
// This is a wrapper function that will ensure 'tlsContainerStorage' is constructed on first use.
|
|
|
|
|
// For more information: http://www.parashift.com/c++-faq/static-init-order-on-first-use.html
|
|
|
|
|
static TLSContainerStorage& getTLSContainerStorage() |
|
|
|
|
// Create global TLS storage object
|
|
|
|
|
static TlsStorage &getTlsStorage() |
|
|
|
|
{ |
|
|
|
|
CV_SINGLETON_LAZY_INIT_REF(TLSContainerStorage, new TLSContainerStorage()) |
|
|
|
|
CV_SINGLETON_LAZY_INIT_REF(TlsStorage, new TlsStorage()) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
TLSDataContainer::TLSDataContainer() |
|
|
|
|
: key_(-1) |
|
|
|
|
{ |
|
|
|
|
key_ = getTLSContainerStorage().allocateKey(this); |
|
|
|
|
key_ = (int)getTlsStorage().reserveSlot(); // Reserve key from TLS storage
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
TLSDataContainer::~TLSDataContainer() |
|
|
|
|
{ |
|
|
|
|
getTLSContainerStorage().releaseKey(key_, this); |
|
|
|
|
key_ = -1; |
|
|
|
|
CV_Assert(key_ == -1); // Key must be released in child object
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void* TLSDataContainer::getData() const |
|
|
|
|
void TLSDataContainer::release() |
|
|
|
|
{ |
|
|
|
|
CV_Assert(key_ >= 0); |
|
|
|
|
TLSStorage* tlsData = TLSStorage::get(); |
|
|
|
|
void* data = tlsData->getData(key_); |
|
|
|
|
if (!data) |
|
|
|
|
{ |
|
|
|
|
data = this->createDataInstance(); |
|
|
|
|
CV_DbgAssert(data != NULL); |
|
|
|
|
tlsData->setData(key_, data); |
|
|
|
|
} |
|
|
|
|
return data; |
|
|
|
|
std::vector<void*> data; |
|
|
|
|
data.reserve(32); |
|
|
|
|
getTlsStorage().releaseSlot(key_, data); // Release key and get stored data for proper destruction
|
|
|
|
|
for(size_t i = 0; i < data.size(); i++) // Delete all assosiated data
|
|
|
|
|
deleteDataInstance(data[i]); |
|
|
|
|
key_ = -1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
TLSStorage::~TLSStorage() |
|
|
|
|
void* TLSDataContainer::getData() const |
|
|
|
|
{ |
|
|
|
|
for (int i = 0; i < (int)tlsData_.size(); i++) |
|
|
|
|
void* pData = getTlsStorage().getData(key_); // Check if data was already allocated
|
|
|
|
|
if(!pData) |
|
|
|
|
{ |
|
|
|
|
void*& data = tlsData_[i]; |
|
|
|
|
if (data) |
|
|
|
|
{ |
|
|
|
|
getTLSContainerStorage().destroyData(i, data); |
|
|
|
|
data = NULL; |
|
|
|
|
} |
|
|
|
|
// Create new data instance and save it to TLS storage
|
|
|
|
|
pData = createDataInstance(); |
|
|
|
|
getTlsStorage().setData(key_, pData); |
|
|
|
|
} |
|
|
|
|
tlsData_.clear(); |
|
|
|
|
return pData; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
TLSData<CoreTLSData>& getCoreTlsData() |
|
|
|
|
{ |
|
|
|
|
CV_SINGLETON_LAZY_INIT_REF(TLSData<CoreTLSData>, new TLSData<CoreTLSData>()) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#if defined CVAPI_EXPORTS && defined WIN32 && !defined WINCE |
|
|
|
|
#ifdef WINRT |
|
|
|
|
#pragma warning(disable:4447) // Disable warning 'main' signature found without threading model
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
extern "C" |
|
|
|
|
BOOL WINAPI DllMain(HINSTANCE, DWORD fdwReason, LPVOID lpReserved) |
|
|
|
|
{ |
|
|
|
|
if (fdwReason == DLL_THREAD_DETACH || fdwReason == DLL_PROCESS_DETACH) |
|
|
|
|
{ |
|
|
|
|
if (lpReserved != NULL) // called after ExitProcess() call
|
|
|
|
|
{ |
|
|
|
|
cv::__termination = true; |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
// Not allowed to free resources if lpReserved is non-null
|
|
|
|
|
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms682583.aspx
|
|
|
|
|
cv::deleteThreadAllocData(); |
|
|
|
|
cv::getTlsStorage().releaseThread(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return TRUE; |
|
|
|
|
} |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
#ifdef CV_COLLECT_IMPL_DATA |
|
|
|
|
ImplCollector& getImplData() |
|
|
|
|