diff --git a/modules/core/include/opencv2/core/core.hpp b/modules/core/include/opencv2/core/core.hpp index b6426d898c..cafba0f8f3 100644 --- a/modules/core/include/opencv2/core/core.hpp +++ b/modules/core/include/opencv2/core/core.hpp @@ -4821,6 +4821,32 @@ private: AutoLock& operator = (const AutoLock&); }; +class TLSDataContainer +{ +private: + int key_; +protected: + CV_EXPORTS TLSDataContainer(); + CV_EXPORTS ~TLSDataContainer(); // virtual is not required +public: + virtual void* createDataInstance() const = 0; + virtual void deleteDataInstance(void* data) const = 0; + + CV_EXPORTS void* getData() const; +}; + +template +class TLSData : protected TLSDataContainer +{ +public: + inline TLSData() {} + inline ~TLSData() {} + inline T* get() const { return (T*)getData(); } +private: + virtual void* createDataInstance() const { return new T; } + virtual void deleteDataInstance(void* data) const { delete (T*)data; } +}; + } #endif // __cplusplus diff --git a/modules/core/src/system.cpp b/modules/core/src/system.cpp index 88f13d622a..b301d95dba 100644 --- a/modules/core/src/system.cpp +++ b/modules/core/src/system.cpp @@ -806,24 +806,6 @@ cvGetModuleInfo( const char* name, const char **version, const char **plugin_lis *plugin_list = plugin_list_buf; } -#if defined CVAPI_EXPORTS && defined WIN32 && !defined WINCE -#ifdef HAVE_WINRT - #pragma warning(disable:4447) // Disable warning 'main' signature found without threading model -#endif - -BOOL WINAPI DllMain( HINSTANCE, DWORD fdwReason, LPVOID ); - -BOOL WINAPI DllMain( HINSTANCE, DWORD fdwReason, LPVOID ) -{ - if( fdwReason == DLL_THREAD_DETACH || fdwReason == DLL_PROCESS_DETACH ) - { - cv::deleteThreadAllocData(); - cv::deleteThreadRNGData(); - } - return TRUE; -} -#endif - namespace cv { @@ -941,6 +923,223 @@ void Mutex::lock() { impl->lock(); } void Mutex::unlock() { impl->unlock(); } 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 +#pragma warning(disable:4505) // unreferenced local function has been removed + +#ifdef HAVE_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) +#endif + static DWORD tlsKey = TLS_OUT_OF_INDEXES; + + static void deleteThreadData() + { + if(tlsKey != TLS_OUT_OF_INDEXES) + { + delete (TLSStorage*)TlsGetValue(tlsKey); + TlsSetValue(tlsKey, NULL); + } + } + + inline TLSStorage* TLSStorage::get() + { + 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; + } +#endif //HAVE_WINRT + +#if defined CVAPI_EXPORTS && defined WIN32 && !defined WINCE +#ifdef HAVE_WINRT + #pragma warning(disable:4447) // Disable warning 'main' signature found without threading model +#endif + +BOOL WINAPI DllMain(HINSTANCE, DWORD fdwReason, LPVOID); + +BOOL WINAPI DllMain(HINSTANCE, DWORD fdwReason, LPVOID) +{ + if (fdwReason == DLL_THREAD_DETACH || fdwReason == DLL_PROCESS_DETACH) + { + cv::deleteThreadAllocData(); + cv::deleteThreadRNGData(); + cv::deleteThreadData(); + } + 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! + } + } +}; +static TLSContainerStorage tlsContainerStorage; + +TLSDataContainer::TLSDataContainer() + : key_(-1) +{ + key_ = tlsContainerStorage.allocateKey(this); } +TLSDataContainer::~TLSDataContainer() +{ + tlsContainerStorage.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) + { + tlsContainerStorage.destroyData(i, data); + data = NULL; + } + } + tlsData_.clear(); +} + +} // namespace cv + /* End of file. */ diff --git a/modules/ocl/src/cl_context.cpp b/modules/ocl/src/cl_context.cpp index ef9611d9b2..7ab39cb9ec 100644 --- a/modules/ocl/src/cl_context.cpp +++ b/modules/ocl/src/cl_context.cpp @@ -57,6 +57,10 @@ namespace cv { namespace ocl { +#if defined(WIN32) +static bool __termination = false; +#endif + struct __Module { __Module(); @@ -494,6 +498,38 @@ PlatformInfo::PlatformInfo() // nothing } +class ContextImpl; + +struct CommandQueue +{ + ContextImpl* context_; + cl_command_queue clQueue_; + + CommandQueue() : context_(NULL), clQueue_(NULL) { } + ~CommandQueue() { release(); } + + void create(ContextImpl* context_); + void release() + { +#ifdef WIN32 + // if process is on termination stage (ExitProcess was called and other threads were terminated) + // then disable command queue release because it may cause program hang + if (!__termination) +#endif + { + if(clQueue_) + { + openCLSafeCall(clReleaseCommandQueue(clQueue_)); // some cleanup problems are here + } + + } + clQueue_ = NULL; + context_ = NULL; + } +}; + +cv::TLSData commandQueueTLSData; + //////////////////////////////// OpenCL context //////////////////////// //This is a global singleton class used to represent a OpenCL context. class ContextImpl : public Context @@ -501,12 +537,11 @@ class ContextImpl : public Context public: const cl_device_id clDeviceID; cl_context clContext; - cl_command_queue clCmdQueue; const DeviceInfo& deviceInfo; protected: ContextImpl(const DeviceInfo& deviceInfo, cl_device_id clDeviceID) - : clDeviceID(clDeviceID), clContext(NULL), clCmdQueue(NULL), deviceInfo(deviceInfo) + : clDeviceID(clDeviceID), clContext(NULL), deviceInfo(deviceInfo) { // nothing } @@ -581,7 +616,13 @@ const void* Context::getOpenCLContextPtr() const const void* Context::getOpenCLCommandQueuePtr() const { - return &(((ContextImpl*)this)->clCmdQueue); + ContextImpl* pThis = (ContextImpl*)this; + CommandQueue* commandQueue = commandQueueTLSData.get(); + if (commandQueue->context_ != pThis) + { + commandQueue->create(pThis); + } + return &commandQueue->clQueue_; } const void* Context::getOpenCLDeviceIDPtr() const @@ -607,10 +648,6 @@ bool ContextImpl::supportsFeature(FEATURE_TYPE featureType) const return false; } -#if defined(WIN32) -static bool __termination = false; -#endif - ContextImpl::~ContextImpl() { #ifdef WIN32 @@ -619,17 +656,11 @@ ContextImpl::~ContextImpl() if (!__termination) #endif { - if(clCmdQueue) - { - openCLSafeCall(clReleaseCommandQueue(clCmdQueue)); // some cleanup problems are here - } - if(clContext) { openCLSafeCall(clReleaseContext(clContext)); } } - clCmdQueue = NULL; clContext = NULL; } @@ -667,12 +698,8 @@ void ContextImpl::setContext(const DeviceInfo* deviceInfo) cl_context_properties cps[3] = { CL_CONTEXT_PLATFORM, (cl_context_properties)(infoImpl.platform_id), 0 }; cl_context clContext = clCreateContext(cps, 1, &infoImpl.device_id, NULL, NULL, &status); openCLVerifyCall(status); - // TODO add CL_QUEUE_PROFILING_ENABLE - cl_command_queue clCmdQueue = clCreateCommandQueue(clContext, infoImpl.device_id, 0, &status); - openCLVerifyCall(status); ContextImpl* ctx = new ContextImpl(infoImpl.info, infoImpl.device_id); - ctx->clCmdQueue = clCmdQueue; ctx->clContext = clContext; ContextImpl* old = NULL; @@ -687,6 +714,17 @@ void ContextImpl::setContext(const DeviceInfo* deviceInfo) } } +void CommandQueue::create(ContextImpl* context) +{ + release(); + cl_int status = 0; + // TODO add CL_QUEUE_PROFILING_ENABLE + cl_command_queue clCmdQueue = clCreateCommandQueue(context->clContext, context->clDeviceID, 0, &status); + openCLVerifyCall(status); + context_ = context; + clQueue_ = clCmdQueue; +} + int getOpenCLPlatforms(PlatformsInfo& platforms) { if (!__initialized)