Merge pull request #1976 from alalek:ocl_multiple_command_queues

This commit is contained in:
Andrey Pavlenko 2013-12-12 09:55:33 +04:00 committed by OpenCV Buildbot
commit 658282fcbe
3 changed files with 298 additions and 35 deletions

View File

@ -4821,6 +4821,32 @@ private:
AutoLock& operator = (const AutoLock&); 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 <typename T>
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 #endif // __cplusplus

View File

@ -806,24 +806,6 @@ cvGetModuleInfo( const char* name, const char **version, const char **plugin_lis
*plugin_list = plugin_list_buf; *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 namespace cv
{ {
@ -941,6 +923,223 @@ void Mutex::lock() { impl->lock(); }
void Mutex::unlock() { impl->unlock(); } void Mutex::unlock() { impl->unlock(); }
bool Mutex::trylock() { return impl->trylock(); } bool Mutex::trylock() { return impl->trylock(); }
//////////////////////////////// thread-local storage ////////////////////////////////
class TLSStorage
{
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();
};
#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<TLSDataContainer*> 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. */ /* End of file. */

View File

@ -57,6 +57,10 @@
namespace cv { namespace cv {
namespace ocl { namespace ocl {
#if defined(WIN32)
static bool __termination = false;
#endif
struct __Module struct __Module
{ {
__Module(); __Module();
@ -494,6 +498,38 @@ PlatformInfo::PlatformInfo()
// nothing // 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<CommandQueue> commandQueueTLSData;
//////////////////////////////// OpenCL context //////////////////////// //////////////////////////////// OpenCL context ////////////////////////
//This is a global singleton class used to represent a OpenCL context. //This is a global singleton class used to represent a OpenCL context.
class ContextImpl : public Context class ContextImpl : public Context
@ -501,12 +537,11 @@ class ContextImpl : public Context
public: public:
const cl_device_id clDeviceID; const cl_device_id clDeviceID;
cl_context clContext; cl_context clContext;
cl_command_queue clCmdQueue;
const DeviceInfo& deviceInfo; const DeviceInfo& deviceInfo;
protected: protected:
ContextImpl(const DeviceInfo& deviceInfo, cl_device_id clDeviceID) ContextImpl(const DeviceInfo& deviceInfo, cl_device_id clDeviceID)
: clDeviceID(clDeviceID), clContext(NULL), clCmdQueue(NULL), deviceInfo(deviceInfo) : clDeviceID(clDeviceID), clContext(NULL), deviceInfo(deviceInfo)
{ {
// nothing // nothing
} }
@ -581,7 +616,13 @@ const void* Context::getOpenCLContextPtr() const
const void* Context::getOpenCLCommandQueuePtr() 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 const void* Context::getOpenCLDeviceIDPtr() const
@ -607,10 +648,6 @@ bool ContextImpl::supportsFeature(FEATURE_TYPE featureType) const
return false; return false;
} }
#if defined(WIN32)
static bool __termination = false;
#endif
ContextImpl::~ContextImpl() ContextImpl::~ContextImpl()
{ {
#ifdef WIN32 #ifdef WIN32
@ -619,17 +656,11 @@ ContextImpl::~ContextImpl()
if (!__termination) if (!__termination)
#endif #endif
{ {
if(clCmdQueue)
{
openCLSafeCall(clReleaseCommandQueue(clCmdQueue)); // some cleanup problems are here
}
if(clContext) if(clContext)
{ {
openCLSafeCall(clReleaseContext(clContext)); openCLSafeCall(clReleaseContext(clContext));
} }
} }
clCmdQueue = NULL;
clContext = 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_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); cl_context clContext = clCreateContext(cps, 1, &infoImpl.device_id, NULL, NULL, &status);
openCLVerifyCall(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); ContextImpl* ctx = new ContextImpl(infoImpl.info, infoImpl.device_id);
ctx->clCmdQueue = clCmdQueue;
ctx->clContext = clContext; ctx->clContext = clContext;
ContextImpl* old = NULL; 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) int getOpenCLPlatforms(PlatformsInfo& platforms)
{ {
if (!__initialized) if (!__initialized)