Merge pull request #15692 from alalek:core_tls_handle_thread_termination

This commit is contained in:
Alexander Alekhin
2019-10-29 20:40:35 +00:00
14 changed files with 650 additions and 239 deletions
@@ -53,6 +53,10 @@
#include <opencv2/core/utils/trace.hpp>
#ifdef ENABLE_INSTRUMENTATION
#include "opencv2/core/utils/instrumentation.hpp"
#endif
#ifdef HAVE_EIGEN
# if defined __GNUC__ && defined __APPLE__
# pragma GCC diagnostic ignored "-Wshadow"
+7 -161
View File
@@ -63,30 +63,6 @@
namespace cv
{
#ifdef CV_COLLECT_IMPL_DATA
CV_EXPORTS void setImpl(int flags); // set implementation flags and reset storage arrays
CV_EXPORTS void addImpl(int flag, const char* func = 0); // add implementation and function name to storage arrays
// Get stored implementation flags and functions names arrays
// Each implementation entry correspond to function name entry, so you can find which implementation was executed in which function
CV_EXPORTS int getImpl(std::vector<int> &impl, std::vector<String> &funName);
CV_EXPORTS bool useCollection(); // return implementation collection state
CV_EXPORTS void setUseCollection(bool flag); // set implementation collection state
#define CV_IMPL_PLAIN 0x01 // native CPU OpenCV implementation
#define CV_IMPL_OCL 0x02 // OpenCL implementation
#define CV_IMPL_IPP 0x04 // IPP implementation
#define CV_IMPL_MT 0x10 // multithreaded implementation
#define CV_IMPL_ADD(impl) \
if(cv::useCollection()) \
{ \
cv::addImpl(impl, CV_Func); \
}
#else
#define CV_IMPL_ADD(impl)
#endif
//! @addtogroup core_utils
//! @{
@@ -726,61 +702,6 @@ private:
AutoLock& operator = (const AutoLock&);
};
// TLS interface
class CV_EXPORTS TLSDataContainer
{
protected:
TLSDataContainer();
virtual ~TLSDataContainer();
void gatherData(std::vector<void*> &data) const;
#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_;
public:
void cleanup(); //! Release created TLS data container objects. It is similar to release() call, but it keeps TLS container valid.
};
// Main TLS data class
template <typename T>
class TLSData : protected TLSDataContainer
{
public:
inline TLSData() {}
inline ~TLSData() { release(); } // Release key and delete associated data
inline T* get() const { return (T*)getData(); } // Get data associated with key
inline T& getRef() const { T* ptr = (T*)getData(); CV_Assert(ptr); return *ptr; } // Get data associated with key
// Get data from all threads
inline void gather(std::vector<T*> &data) const
{
std::vector<void*> &dataVoid = reinterpret_cast<std::vector<void*>&>(data);
gatherData(dataVoid);
}
inline void cleanup() { TLSDataContainer::cleanup(); }
private:
virtual void* createDataInstance() const CV_OVERRIDE {return new T;} // Wrapper to allocate data by template
virtual void deleteDataInstance(void* pData) const CV_OVERRIDE {delete (T*)pData;} // Wrapper to release data by template
// Disable TLS copy operations
TLSData(TLSData &) {}
TLSData& operator =(const TLSData &) {return *this;}
};
/** @brief Designed for command line parsing
@@ -1199,88 +1120,6 @@ public:
std::vector<Node<OBJECT>*> m_childs;
};
// Instrumentation external interface
namespace instr
{
#if !defined OPENCV_ABI_CHECK
enum TYPE
{
TYPE_GENERAL = 0, // OpenCV API function, e.g. exported function
TYPE_MARKER, // Information marker
TYPE_WRAPPER, // Wrapper function for implementation
TYPE_FUN, // Simple function call
};
enum IMPL
{
IMPL_PLAIN = 0,
IMPL_IPP,
IMPL_OPENCL,
};
struct NodeDataTls
{
NodeDataTls()
{
m_ticksTotal = 0;
}
uint64 m_ticksTotal;
};
class CV_EXPORTS NodeData
{
public:
NodeData(const char* funName = 0, const char* fileName = NULL, int lineNum = 0, void* retAddress = NULL, bool alwaysExpand = false, cv::instr::TYPE instrType = TYPE_GENERAL, cv::instr::IMPL implType = IMPL_PLAIN);
NodeData(NodeData &ref);
~NodeData();
NodeData& operator=(const NodeData&);
cv::String m_funName;
cv::instr::TYPE m_instrType;
cv::instr::IMPL m_implType;
const char* m_fileName;
int m_lineNum;
void* m_retAddress;
bool m_alwaysExpand;
bool m_funError;
volatile int m_counter;
volatile uint64 m_ticksTotal;
TLSData<NodeDataTls> m_tls;
int m_threads;
// No synchronization
double getTotalMs() const { return ((double)m_ticksTotal / cv::getTickFrequency()) * 1000; }
double getMeanMs() const { return (((double)m_ticksTotal/m_counter) / cv::getTickFrequency()) * 1000; }
};
bool operator==(const NodeData& lhs, const NodeData& rhs);
typedef Node<NodeData> InstrNode;
CV_EXPORTS InstrNode* getTrace();
#endif // !defined OPENCV_ABI_CHECK
CV_EXPORTS bool useInstrumentation();
CV_EXPORTS void setUseInstrumentation(bool flag);
CV_EXPORTS void resetTrace();
enum FLAGS
{
FLAGS_NONE = 0,
FLAGS_MAPPING = 0x01,
FLAGS_EXPAND_SAME_NAMES = 0x02,
};
CV_EXPORTS void setFlags(FLAGS modeFlags);
static inline void setFlags(int modeFlags) { setFlags((FLAGS)modeFlags); }
CV_EXPORTS FLAGS getFlags();
} // namespace instr
namespace samples {
@@ -1355,6 +1194,13 @@ CV_EXPORTS int getThreadID();
} //namespace cv
#ifdef CV_COLLECT_IMPL_DATA
#include "opencv2/core/utils/instrumentation.hpp"
#else
/// Collect implementation data on OpenCV function call. Requires ENABLE_IMPL_COLLECTION build option.
#define CV_IMPL_ADD(impl)
#endif
#ifndef DISABLE_OPENCV_24_COMPATIBILITY
#include "opencv2/core/core_c.h"
#endif
@@ -0,0 +1,125 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
#ifndef OPENCV_UTILS_INSTR_HPP
#define OPENCV_UTILS_INSTR_HPP
#include <opencv2/core/utility.hpp>
#include <opencv2/core/utils/tls.hpp>
namespace cv {
//! @addtogroup core_utils
//! @{
#ifdef CV_COLLECT_IMPL_DATA
CV_EXPORTS void setImpl(int flags); // set implementation flags and reset storage arrays
CV_EXPORTS void addImpl(int flag, const char* func = 0); // add implementation and function name to storage arrays
// Get stored implementation flags and functions names arrays
// Each implementation entry correspond to function name entry, so you can find which implementation was executed in which function
CV_EXPORTS int getImpl(std::vector<int> &impl, std::vector<String> &funName);
CV_EXPORTS bool useCollection(); // return implementation collection state
CV_EXPORTS void setUseCollection(bool flag); // set implementation collection state
#define CV_IMPL_PLAIN 0x01 // native CPU OpenCV implementation
#define CV_IMPL_OCL 0x02 // OpenCL implementation
#define CV_IMPL_IPP 0x04 // IPP implementation
#define CV_IMPL_MT 0x10 // multithreaded implementation
#undef CV_IMPL_ADD
#define CV_IMPL_ADD(impl) \
if(cv::useCollection()) \
{ \
cv::addImpl(impl, CV_Func); \
}
#endif
// Instrumentation external interface
namespace instr
{
#if !defined OPENCV_ABI_CHECK
enum TYPE
{
TYPE_GENERAL = 0, // OpenCV API function, e.g. exported function
TYPE_MARKER, // Information marker
TYPE_WRAPPER, // Wrapper function for implementation
TYPE_FUN, // Simple function call
};
enum IMPL
{
IMPL_PLAIN = 0,
IMPL_IPP,
IMPL_OPENCL,
};
struct NodeDataTls
{
NodeDataTls()
{
m_ticksTotal = 0;
}
uint64 m_ticksTotal;
};
class CV_EXPORTS NodeData
{
public:
NodeData(const char* funName = 0, const char* fileName = NULL, int lineNum = 0, void* retAddress = NULL, bool alwaysExpand = false, cv::instr::TYPE instrType = TYPE_GENERAL, cv::instr::IMPL implType = IMPL_PLAIN);
NodeData(NodeData &ref);
~NodeData();
NodeData& operator=(const NodeData&);
cv::String m_funName;
cv::instr::TYPE m_instrType;
cv::instr::IMPL m_implType;
const char* m_fileName;
int m_lineNum;
void* m_retAddress;
bool m_alwaysExpand;
bool m_funError;
volatile int m_counter;
volatile uint64 m_ticksTotal;
TLSDataAccumulator<NodeDataTls> m_tls;
int m_threads;
// No synchronization
double getTotalMs() const { return ((double)m_ticksTotal / cv::getTickFrequency()) * 1000; }
double getMeanMs() const { return (((double)m_ticksTotal/m_counter) / cv::getTickFrequency()) * 1000; }
};
bool operator==(const NodeData& lhs, const NodeData& rhs);
typedef Node<NodeData> InstrNode;
CV_EXPORTS InstrNode* getTrace();
#endif // !defined OPENCV_ABI_CHECK
CV_EXPORTS bool useInstrumentation();
CV_EXPORTS void setUseInstrumentation(bool flag);
CV_EXPORTS void resetTrace();
enum FLAGS
{
FLAGS_NONE = 0,
FLAGS_MAPPING = 0x01,
FLAGS_EXPAND_SAME_NAMES = 0x02,
};
CV_EXPORTS void setFlags(FLAGS modeFlags);
static inline void setFlags(int modeFlags) { setFlags((FLAGS)modeFlags); }
CV_EXPORTS FLAGS getFlags();
} // namespace instr
//! @}
} // namespace
#endif // OPENCV_UTILS_TLS_HPP
@@ -0,0 +1,237 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
#ifndef OPENCV_UTILS_TLS_HPP
#define OPENCV_UTILS_TLS_HPP
#include <opencv2/core/utility.hpp>
namespace cv {
//! @addtogroup core_utils
//! @{
namespace details { class TlsStorage; }
/** TLS container base implementation
*
* Don't use directly.
*
* @sa TLSData, TLSDataAccumulator templates
*/
class CV_EXPORTS TLSDataContainer
{
protected:
TLSDataContainer();
virtual ~TLSDataContainer();
/// @deprecated use detachData() instead
void gatherData(std::vector<void*> &data) const;
/// get TLS data and detach all data from threads (similar to cleanup() call)
void detachData(std::vector<void*>& data);
void* getData() const;
void release();
protected:
virtual void* createDataInstance() const = 0;
virtual void deleteDataInstance(void* pData) const = 0;
#if OPENCV_ABI_COMPATIBILITY > 300
private:
#else
public:
#endif
int key_;
friend class cv::details::TlsStorage; // core/src/system.cpp
public:
void cleanup(); //!< Release created TLS data container objects. It is similar to release() call, but it keeps TLS container valid.
private:
// Disable copy/assign (noncopyable pattern)
TLSDataContainer(TLSDataContainer &);
TLSDataContainer& operator =(const TLSDataContainer &);
};
/** @brief Simple TLS data class
*
* @sa TLSDataAccumulator
*/
template <typename T>
class TLSData : protected TLSDataContainer
{
public:
inline TLSData() {}
inline ~TLSData() { release(); }
inline T* get() const { return (T*)getData(); } //!< Get data associated with key
inline T& getRef() const { T* ptr = (T*)getData(); CV_DbgAssert(ptr); return *ptr; } //!< Get data associated with key
/// Release associated thread data
inline void cleanup()
{
TLSDataContainer::cleanup();
}
protected:
/// Wrapper to allocate data by template
virtual void* createDataInstance() const CV_OVERRIDE { return new T; }
/// Wrapper to release data by template
virtual void deleteDataInstance(void* pData) const CV_OVERRIDE { delete (T*)pData; }
};
/// TLS data accumulator with gathering methods
template <typename T>
class TLSDataAccumulator : public TLSData<T>
{
mutable cv::Mutex mutex;
mutable std::vector<T*> dataFromTerminatedThreads;
std::vector<T*> detachedData;
bool cleanupMode;
public:
TLSDataAccumulator() : cleanupMode(false) {}
~TLSDataAccumulator()
{
release();
}
/** @brief Get data from all threads
* @deprecated replaced by detachData()
*
* Lifetime of vector data is valid until next detachData()/cleanup()/release() calls
*
* @param[out] data result buffer (should be empty)
*/
void gather(std::vector<T*> &data) const
{
CV_Assert(cleanupMode == false); // state is not valid
CV_Assert(data.empty());
{
std::vector<void*> &dataVoid = reinterpret_cast<std::vector<void*>&>(data);
TLSDataContainer::gatherData(dataVoid);
}
{
AutoLock lock(mutex);
data.reserve(data.size() + dataFromTerminatedThreads.size());
for (typename std::vector<T*>::const_iterator i = dataFromTerminatedThreads.begin(); i != dataFromTerminatedThreads.end(); ++i)
{
data.push_back((T*)*i);
}
}
}
/** @brief Get and detach data from all threads
*
* Call cleanupDetachedData() when returned vector is not needed anymore.
*
* @return Vector with associated data. Content is preserved (including lifetime of attached data pointers) until next detachData()/cleanupDetachedData()/cleanup()/release() calls
*/
std::vector<T*>& detachData()
{
CV_Assert(cleanupMode == false); // state is not valid
std::vector<void*> dataVoid;
{
TLSDataContainer::detachData(dataVoid);
}
{
AutoLock lock(mutex);
detachedData.reserve(dataVoid.size() + dataFromTerminatedThreads.size());
for (typename std::vector<T*>::const_iterator i = dataFromTerminatedThreads.begin(); i != dataFromTerminatedThreads.end(); ++i)
{
detachedData.push_back((T*)*i);
}
dataFromTerminatedThreads.clear();
for (typename std::vector<void*>::const_iterator i = dataVoid.begin(); i != dataVoid.end(); ++i)
{
detachedData.push_back((T*)(void*)*i);
}
}
dataVoid.clear();
return detachedData;
}
/// Release associated thread data returned by detachData() call
void cleanupDetachedData()
{
AutoLock lock(mutex);
cleanupMode = true;
_cleanupDetachedData();
cleanupMode = false;
}
/// Release associated thread data
void cleanup()
{
cleanupMode = true;
TLSDataContainer::cleanup();
AutoLock lock(mutex);
_cleanupDetachedData();
_cleanupTerminatedData();
cleanupMode = false;
}
/// Release associated thread data and free TLS key
void release()
{
cleanupMode = true;
TLSDataContainer::release();
{
AutoLock lock(mutex);
_cleanupDetachedData();
_cleanupTerminatedData();
}
}
protected:
// synchronized
void _cleanupDetachedData()
{
for (typename std::vector<T*>::iterator i = detachedData.begin(); i != detachedData.end(); ++i)
{
deleteDataInstance((T*)*i);
}
detachedData.clear();
}
// synchronized
void _cleanupTerminatedData()
{
for (typename std::vector<T*>::iterator i = dataFromTerminatedThreads.begin(); i != dataFromTerminatedThreads.end(); ++i)
{
deleteDataInstance((T*)*i);
}
dataFromTerminatedThreads.clear();
}
protected:
virtual void* createDataInstance() const CV_OVERRIDE
{
// Note: we can collect all allocated data here, but this would require raced mutex locks
return new T;
}
virtual void deleteDataInstance(void* pData) const CV_OVERRIDE
{
if (cleanupMode)
{
delete (T*)pData;
}
else
{
AutoLock lock(mutex);
dataFromTerminatedThreads.push_back((T*)pData);
}
}
};
//! @}
} // namespace
#endif // OPENCV_UTILS_TLS_HPP
@@ -9,6 +9,8 @@
#include <opencv2/core/utils/logger.hpp>
#include <opencv2/core/utils/tls.hpp>
#include "trace.hpp"
//! @cond IGNORED
@@ -332,7 +334,7 @@ public:
Mutex mutexCreate;
Mutex mutexCount;
TLSData<TraceManagerThreadLocal> tls;
TLSDataAccumulator<TraceManagerThreadLocal> tls;
cv::Ptr<TraceStorage> trace_storage;
private: