// 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 #ifndef OPENCV_CORE_UTILITY_H #error "tls.hpp must be included after opencv2/core/utility.hpp or opencv2/core.hpp" #endif 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 &data) const; /// get TLS data and detach all data from threads (similar to cleanup() call) void detachData(std::vector& data); void* getData() const; void release(); protected: virtual void* createDataInstance() const = 0; virtual void deleteDataInstance(void* pData) const = 0; private: 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 &) = delete; TLSDataContainer& operator =(const TLSDataContainer &) = delete; }; /** @brief Simple TLS data class * * @sa TLSDataAccumulator */ template 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 class TLSDataAccumulator : public TLSData { mutable cv::Mutex mutex; mutable std::vector dataFromTerminatedThreads; std::vector 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 &data) const { CV_Assert(cleanupMode == false); // state is not valid CV_Assert(data.empty()); { std::vector &dataVoid = reinterpret_cast&>(data); TLSDataContainer::gatherData(dataVoid); } { AutoLock lock(mutex); data.reserve(data.size() + dataFromTerminatedThreads.size()); for (typename std::vector::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& detachData() { CV_Assert(cleanupMode == false); // state is not valid std::vector dataVoid; { TLSDataContainer::detachData(dataVoid); } { AutoLock lock(mutex); detachedData.reserve(dataVoid.size() + dataFromTerminatedThreads.size()); for (typename std::vector::const_iterator i = dataFromTerminatedThreads.begin(); i != dataFromTerminatedThreads.end(); ++i) { detachedData.push_back((T*)*i); } dataFromTerminatedThreads.clear(); for (typename std::vector::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::iterator i = detachedData.begin(); i != detachedData.end(); ++i) { deleteDataInstance((T*)*i); } detachedData.clear(); } // synchronized void _cleanupTerminatedData() { for (typename std::vector::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