// Copyright (c) 2023 PaddlePaddle Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #pragma once #include #include #include #include #include #include #include #include "glog/logging.h" namespace phi { #ifdef __cpp_lib_void_t using std::void_t; #else template using void_t = void; #endif template struct IsAccumulatable : std::false_type {}; template <> struct IsAccumulatable : std::false_type {}; template struct IsAccumulatable() += std::declval())>> : std::true_type {}; template class ThreadDataRegistry { public: // Singleton static ThreadDataRegistry& GetInstance() { static ThreadDataRegistry instance; return instance; } T* GetMutableCurrentThreadData() { return &CurrentThreadData(); } const T& GetCurrentThreadData() { return CurrentThreadData(); } template ::value>> void SetCurrentThreadData(const T& val) { CurrentThreadData() = val; } // Returns current snapshot of all threads. Make sure there is no thread // create/destroy when using it. template < typename Alias = T, typename = std::enable_if_t::value>> std::unordered_map GetAllThreadDataByValue() { return impl_->GetAllThreadDataByValue(); } // Returns current snapshot of all threads. Make sure there is no thread // create/destroy when using it. std::unordered_map> GetAllThreadDataByRef() { return impl_->GetAllThreadDataByRef(); } private: // types // Lock types #if defined(__clang__) || defined(__GNUC__) // CLANG or GCC #ifndef __APPLE__ using LockType = std::shared_mutex; using SharedLockGuardType = std::shared_lock; // Special case : mac. https://github.com/facebook/react-native/issues/31250 #else using LockType = std::mutex; using SharedLockGuardType = std::lock_guard; #endif #elif defined(_MSC_VER) // MSVC using LockType = std::shared_mutex; using SharedLockGuardType = std::shared_lock; #else // other compilers using LockType = std::mutex; using SharedLockGuardType = std::lock_guard; #endif class ThreadDataHolder; class ThreadDataRegistryImpl { public: void RegisterData(uint64_t tid, ThreadDataHolder* tls_obj) { std::lock_guard guard(lock_); tid_map_[tid] = tls_obj; } template void AccumulateToAnotherThread(...) {} template ::value>> void AccumulateToAnotherThread(uint64_t tid) { auto& data = tid_map_.at(tid)->GetData(); for (auto& kv : tid_map_) { if (kv.first != tid) { auto& data_in_another_thread = kv.second->GetData(); data_in_another_thread += data; VLOG(2) << "Add data " << data << " from thread " << tid << " to " << kv.first << " , after update, data is " << data_in_another_thread << "."; break; } } } void UnregisterData(uint64_t tid) { std::lock_guard guard(lock_); AccumulateToAnotherThread(tid); tid_map_.erase(tid); } template < typename Alias = T, typename = std::enable_if_t::value>> std::unordered_map GetAllThreadDataByValue() { std::unordered_map data_copy; SharedLockGuardType guard(lock_); data_copy.reserve(tid_map_.size()); for (auto& kv : tid_map_) { data_copy.emplace(kv.first, kv.second->GetData()); } return data_copy; } std::unordered_map> GetAllThreadDataByRef() { std::unordered_map> data_ref; SharedLockGuardType guard(lock_); data_ref.reserve(tid_map_.size()); for (auto& kv : tid_map_) { data_ref.emplace(kv.first, std::ref(kv.second->GetData())); } return data_ref; } private: LockType lock_; std::unordered_map tid_map_; // not owned }; class ThreadDataHolder { public: explicit ThreadDataHolder( std::shared_ptr registry) { registry_ = std::move(registry); tid_ = std::hash()(std::this_thread::get_id()); registry_->RegisterData(tid_, this); } ~ThreadDataHolder() { registry_->UnregisterData(tid_); } T& GetData() { return data_; } private: std::shared_ptr registry_; uint64_t tid_; T data_; }; // methods ThreadDataRegistry() { impl_ = std::make_shared(); } ThreadDataRegistry(const ThreadDataRegistry&) = delete; ThreadDataRegistry& operator=(const ThreadDataRegistry&) = delete; T& CurrentThreadData() { static thread_local ThreadDataHolder thread_data(impl_); return thread_data.GetData(); } // data std::shared_ptr impl_; }; } // namespace phi