diff --git a/modules/core/include/opencv2/core/cvdef.h b/modules/core/include/opencv2/core/cvdef.h index 1d933b5c30..9f4a004f96 100644 --- a/modules/core/include/opencv2/core/cvdef.h +++ b/modules/core/include/opencv2/core/cvdef.h @@ -58,6 +58,8 @@ #include "opencv2/hal/defs.h" +#define OPENCV_ABI_COMPATIBILITY 300 + #ifdef __OPENCV_BUILD # define DISABLE_OPENCV_24_COMPATIBILITY #endif diff --git a/modules/core/include/opencv2/core/utility.hpp b/modules/core/include/opencv2/core/utility.hpp index f0b473fac3..870d35012e 100644 --- a/modules/core/include/opencv2/core/utility.hpp +++ b/modules/core/include/opencv2/core/utility.hpp @@ -513,30 +513,42 @@ private: AutoLock& operator = (const AutoLock&); }; +// TLS interface class CV_EXPORTS TLSDataContainer { -private: - int key_; protected: TLSDataContainer(); virtual ~TLSDataContainer(); -public: - virtual void* createDataInstance() const = 0; - virtual void deleteDataInstance(void* data) const = 0; +#if OPENCV_ABI_COMPATIBILITY > 300 void* getData() const; + void release(); + +private: +#else + void release(); + +public: + void* getData() const; +#endif + virtual void* createDataInstance() const = 0; + virtual void deleteDataInstance(void* pData) const = 0; + + int key_; }; +// Main TLS data class template class TLSData : protected TLSDataContainer { public: - inline TLSData() {} - inline ~TLSData() {} - inline T* get() const { return (T*)getData(); } + inline TLSData() {} + inline ~TLSData() { release(); } // Release key and delete associated data + inline T* get() const { return (T*)getData(); } // Get data assosiated with key + private: - virtual void* createDataInstance() const { return new T; } - virtual void deleteDataInstance(void* data) const { delete (T*)data; } + virtual void* createDataInstance() const {return new T;} // Wrapper to allocate data by template + virtual void deleteDataInstance(void* pData) const {delete (T*)pData;} // Wrapper to release data by template }; /** @brief Designed for command line parsing diff --git a/modules/core/src/system.cpp b/modules/core/src/system.cpp index 6a85c40ee3..7631a3dba4 100644 --- a/modules/core/src/system.cpp +++ b/modules/core/src/system.cpp @@ -936,87 +936,254 @@ bool Mutex::trylock() { return impl->trylock(); } //////////////////////////////// thread-local storage //////////////////////////////// -class TLSStorage -{ - std::vector 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(); -}; - #ifdef WIN32 #ifdef _MSC_VER #pragma warning(disable:4505) // unreferenced local function has been removed #endif - -#ifdef WINRT - // using C++11 thread attribute for local thread data - static __declspec( thread ) TLSStorage* g_tlsdata = NULL; - - static void deleteThreadData() - { - if (g_tlsdata) - { - delete g_tlsdata; - g_tlsdata = NULL; - } - } - - inline TLSStorage* TLSStorage::get() - { - if (!g_tlsdata) - { - g_tlsdata = new TLSStorage; - } - return g_tlsdata; - } -#else -#ifdef WINCE -# define TLS_OUT_OF_INDEXES ((DWORD)0xFFFFFFFF) +#ifndef TLS_OUT_OF_INDEXES +#define TLS_OUT_OF_INDEXES ((DWORD)0xFFFFFFFF) +#endif #endif - static DWORD tlsKey = TLS_OUT_OF_INDEXES; - static void deleteThreadData() +// TLS platform abstraction layer +class TlsAbstraction +{ +public: + TlsAbstraction(); + ~TlsAbstraction(); + void* GetData() const; + void SetData(void *pData); + +private: +#ifdef WIN32 +#ifndef WINRT + DWORD tlsKey; +#endif +#else // WIN32 + pthread_key_t tlsKey; +#endif +}; + +#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 +{ + 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 + +// Per-thread data structure +struct ThreadData +{ + ThreadData() { - if(tlsKey != TLS_OUT_OF_INDEXES) - { - delete (TLSStorage*)TlsGetValue(tlsKey); - TlsSetValue(tlsKey, NULL); - } + idx = 0; + slots.reserve(32); } - inline TLSStorage* TLSStorage::get() + std::vector slots; // Data array for a thread + size_t idx; // Thread index in TLS storage. This is not OS thread ID! +}; + +// Main TLS storage class +class TlsStorage +{ +public: + TlsStorage() { - if (tlsKey == TLS_OUT_OF_INDEXES) - { - tlsKey = TlsAlloc(); - CV_Assert(tlsKey != TLS_OUT_OF_INDEXES); - } - TLSStorage* d = (TLSStorage*)TlsGetValue(tlsKey); - if (!d) - { - d = new TLSStorage; - TlsSetValue(tlsKey, d); - } - return d; + tlsSlots = 0; + threads.reserve(32); } -#endif //WINRT + ~TlsStorage() + { + for(size_t i = 0; i < threads.size(); i++) + { + 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]; + } + } + threads.clear(); + } + + void releaseThread() + { + AutoLock guard(mtxGlobalAccess); + ThreadData *pTD = (ThreadData*)tls.GetData(); + for(size_t i = 0; i < threads.size(); i++) + { + if(pTD == threads[i]) + { + threads[i] = 0; + break; + } + } + tls.SetData(0); + delete pTD; + } + + // Reserve TLS storage index + size_t reserveSlot() + { + AutoLock guard(mtxGlobalAccess); + tlsSlots++; + return (tlsSlots-1); + } + + // Release TLS storage index and pass assosiated data to caller + void releaseSlot(size_t slotIdx, std::vector &dataVec) + { + AutoLock guard(mtxGlobalAccess); + CV_Assert(tlsSlots > slotIdx); + + for(size_t i = 0; i < threads.size(); i++) + { + if(threads[i]->slots[slotIdx]) + { + dataVec.push_back(threads[i]->slots[slotIdx]); + threads[i]->slots[slotIdx] = 0; + } + } + // If we removing last element, decriment slots size to save space + if(tlsSlots-1 == slotIdx) + tlsSlots--; + } + + // Get data by TLS storage index + void* getData(size_t slotIdx) const + { + CV_Assert(tlsSlots > slotIdx); + + ThreadData* threadData = (ThreadData*)tls.GetData(); + if(threadData && threadData->slots.size() > slotIdx) + return threadData->slots[slotIdx]; + + return NULL; + } + + // Set data to storage index + void setData(size_t slotIdx, void* pData) + { + CV_Assert(pData != NULL); + + ThreadData* threadData = (ThreadData*)tls.GetData(); + if(!threadData) + { + 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 threads; // Array for all allocated data. Thread data pointers are placed here to allow data cleanup +}; + +// Create global TLS storage object +static TlsStorage &getTlsStorage() +{ + CV_SINGLETON_LAZY_INIT_REF(TlsStorage, new TlsStorage()) +} + +TLSDataContainer::TLSDataContainer() +{ + key_ = (int)getTlsStorage().reserveSlot(); // Reserve key from TLS storage +} + +TLSDataContainer::~TLSDataContainer() +{ + CV_Assert(key_ == -1); // Key must be released in child object +} + +void TLSDataContainer::release() +{ + std::vector 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; +} + +void* TLSDataContainer::getData() const +{ + void* pData = getTlsStorage().getData(key_); // Check if data was already allocated + if(!pData) + { + // Create new data instance and save it to TLS storage + pData = createDataInstance(); + getTlsStorage().setData(key_, pData); + } + return pData; +} + +TLSData& getCoreTlsData() +{ + CV_SINGLETON_LAZY_INIT_REF(TLSData, new TLSData()) +} #if defined CVAPI_EXPORTS && defined WIN32 && !defined WINCE #ifdef WINRT @@ -1037,141 +1204,13 @@ BOOL WINAPI DllMain(HINSTANCE, DWORD fdwReason, LPVOID lpReserved) // 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(); + cv::getTlsStorage().releaseThread(); } } return TRUE; } #endif -#else - static pthread_key_t tlsKey = 0; - static pthread_once_t tlsKeyOnce = PTHREAD_ONCE_INIT; - - static void deleteTLSStorage(void* data) - { - delete (TLSStorage*)data; - } - - static void makeKey() - { - int errcode = pthread_key_create(&tlsKey, deleteTLSStorage); - CV_Assert(errcode == 0); - } - - inline TLSStorage* TLSStorage::get() - { - pthread_once(&tlsKeyOnce, makeKey); - TLSStorage* d = (TLSStorage*)pthread_getspecific(tlsKey); - if( !d ) - { - d = new TLSStorage; - pthread_setspecific(tlsKey, d); - } - return d; - } -#endif - -class TLSContainerStorage -{ - cv::Mutex mutex_; - std::vector tlsContainers_; -public: - TLSContainerStorage() { } - ~TLSContainerStorage() - { - for (size_t i = 0; i < tlsContainers_.size(); i++) - { - CV_DbgAssert(tlsContainers_[i] == NULL); // not all keys released - tlsContainers_[i] = NULL; - } - } - - 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 - } - - void destroyData(int key, void* data) - { - cv::AutoLock lock(mutex_); - TLSDataContainer* k = tlsContainers_[key]; - if (!k) - return; - try - { - k->deleteDataInstance(data); - } - catch (...) - { - CV_DbgAssert(k == NULL); // Debug this! - } - } -}; - -// 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() -{ - CV_SINGLETON_LAZY_INIT_REF(TLSContainerStorage, new TLSContainerStorage()) -} - -TLSDataContainer::TLSDataContainer() - : key_(-1) -{ - key_ = getTLSContainerStorage().allocateKey(this); -} - -TLSDataContainer::~TLSDataContainer() -{ - getTLSContainerStorage().releaseKey(key_, this); - key_ = -1; -} - -void* TLSDataContainer::getData() const -{ - 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; -} - -TLSStorage::~TLSStorage() -{ - for (int i = 0; i < (int)tlsData_.size(); i++) - { - void*& data = tlsData_[i]; - if (data) - { - getTLSContainerStorage().destroyData(i, data); - data = NULL; - } - } - tlsData_.clear(); -} - - -TLSData& getCoreTlsData() -{ - CV_SINGLETON_LAZY_INIT_REF(TLSData, new TLSData()) -} - - #ifdef CV_COLLECT_IMPL_DATA ImplCollector& getImplData() {