| #pragma once | |
| #include <mutex> | |
| #include <unordered_map> | |
| #include <unordered_set> | |
| #include "caffe2/core/logging.h" | |
| namespace caffe2 { | |
| /** | |
| * thread_local pointer in C++ is a per thread pointer. However, sometimes | |
| * we want to have a thread local state that is per thread and also per | |
| * instance. e.g. we have the following class: | |
| * class A { | |
| * ThreadLocalPtr<int> x; | |
| * } | |
| * We would like to have a copy of x per thread and also per instance of class A | |
| * This can be applied to storing per instance thread local state of some class, | |
| * when we could have multiple instances of the class in the same thread. | |
| * We implemented a subset of functions in folly::ThreadLocalPtr that's enough | |
| * to support BlackBoxPredictor. | |
| */ | |
| class ThreadLocalPtrImpl; | |
| class ThreadLocalHelper; | |
| /** | |
| * Map of object pointer to instance in each thread | |
| * to achieve per thread(using thread_local) per object(using the map) | |
| * thread local pointer | |
| */ | |
| typedef std::unordered_map<ThreadLocalPtrImpl*, std::shared_ptr<void>> | |
| UnsafeThreadLocalMap; | |
| ThreadLocalHelper* getThreadLocalHelper(); | |
| typedef std::vector<ThreadLocalHelper*> UnsafeAllThreadLocalHelperVector; | |
| /** | |
| * A thread safe vector of all ThreadLocalHelper, this will be used | |
| * to encapuslate the locking in the APIs for the changes to the global | |
| * AllThreadLocalHelperVector instance. | |
| */ | |
| class AllThreadLocalHelperVector { | |
| public: | |
| AllThreadLocalHelperVector() {} | |
| // Add a new ThreadLocalHelper to the vector | |
| void push_back(ThreadLocalHelper* helper); | |
| // Erase a ThreadLocalHelper to the vector | |
| void erase(ThreadLocalHelper* helper); | |
| // Erase object in all the helpers stored in vector | |
| // Called during destructor of a ThreadLocalPtrImpl | |
| void erase_tlp(ThreadLocalPtrImpl* ptr); | |
| private: | |
| UnsafeAllThreadLocalHelperVector vector_; | |
| std::mutex mutex_; | |
| }; | |
| /** | |
| * ThreadLocalHelper is per thread | |
| */ | |
| class ThreadLocalHelper { | |
| public: | |
| ThreadLocalHelper(); | |
| // When the thread dies, we want to clean up *this* | |
| // in AllThreadLocalHelperVector | |
| ~ThreadLocalHelper(); | |
| // Insert a (object, ptr) pair into the thread local map | |
| void insert(ThreadLocalPtrImpl* tl_ptr, std::shared_ptr<void> ptr); | |
| // Get the ptr by object | |
| void* get(ThreadLocalPtrImpl* key); | |
| // Erase the ptr associated with the object in the map | |
| void erase(ThreadLocalPtrImpl* key); | |
| private: | |
| // mapping of object -> ptr in each thread | |
| UnsafeThreadLocalMap mapping_; | |
| std::mutex mutex_; | |
| }; // ThreadLocalHelper | |
| /** ThreadLocalPtrImpl is per object | |
| */ | |
| class ThreadLocalPtrImpl { | |
| public: | |
| ThreadLocalPtrImpl() {} | |
| // Delete copy and move constructors | |
| ThreadLocalPtrImpl(const ThreadLocalPtrImpl&) = delete; | |
| ThreadLocalPtrImpl(ThreadLocalPtrImpl&&) = delete; | |
| ThreadLocalPtrImpl& operator=(const ThreadLocalPtrImpl&) = delete; | |
| ThreadLocalPtrImpl& operator=(const ThreadLocalPtrImpl&&) = delete; | |
| // In the case when object dies first, we want to | |
| // clean up the states in all child threads | |
| ~ThreadLocalPtrImpl(); | |
| template <typename T> | |
| T* get() { | |
| return static_cast<T*>(getThreadLocalHelper()->get(this)); | |
| } | |
| template <typename T> | |
| void reset(T* newPtr = nullptr) { | |
| VLOG(2) << "In Reset(" << newPtr << ")"; | |
| auto* wrapper = getThreadLocalHelper(); | |
| // Cleaning up the objects(T) stored in the ThreadLocalPtrImpl in the thread | |
| wrapper->erase(this); | |
| if (newPtr != nullptr) { | |
| std::shared_ptr<void> sharedPtr(newPtr); | |
| // Deletion of newPtr is handled by shared_ptr | |
| // as it implements type erasure | |
| wrapper->insert(this, std::move(sharedPtr)); | |
| } | |
| } | |
| }; // ThreadLocalPtrImpl | |
| template <typename T> | |
| class ThreadLocalPtr { | |
| public: | |
| auto* operator->() { | |
| return get(); | |
| } | |
| auto& operator*() { | |
| return *get(); | |
| } | |
| auto* get() { | |
| return impl_.get<T>(); | |
| } | |
| auto* operator->() const { | |
| return get(); | |
| } | |
| auto& operator*() const { | |
| return *get(); | |
| } | |
| auto* get() const { | |
| return impl_.get<T>(); | |
| } | |
| void reset(unique_ptr<T> ptr = nullptr) { | |
| impl_.reset<T>(ptr.release()); | |
| } | |
| private: | |
| ThreadLocalPtrImpl impl_; | |
| }; | |
| } // namespace caffe2 |