TLS memory leaks were fixed;

TLS was redesigned in more straightforward way;
OPENCV_ABI_COMPATIBILITY define was added;
This commit is contained in:
Pavel Vlasov 2015-08-12 16:23:02 +03:00
parent 09b9b0fb9e
commit a33d98c13a
3 changed files with 262 additions and 209 deletions

View File

@ -58,6 +58,8 @@
#include "opencv2/hal/defs.h"
#define OPENCV_ABI_COMPATIBILITY 300
#ifdef __OPENCV_BUILD
# define DISABLE_OPENCV_24_COMPATIBILITY
#endif

View File

@ -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 <typename T>
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

View File

@ -936,87 +936,254 @@ 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
#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<void*> 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<void*> &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<ThreadData*> 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<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;
}
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<CoreTLSData>& getCoreTlsData()
{
CV_SINGLETON_LAZY_INIT_REF(TLSData<CoreTLSData>, new TLSData<CoreTLSData>())
}
#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<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!
}
}
};
// 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<CoreTLSData>& getCoreTlsData()
{
CV_SINGLETON_LAZY_INIT_REF(TLSData<CoreTLSData>, new TLSData<CoreTLSData>())
}
#ifdef CV_COLLECT_IMPL_DATA
ImplCollector& getImplData()
{