200 lines
5.8 KiB
C++
200 lines
5.8 KiB
C++
// 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 <functional>
|
|
#include <memory>
|
|
#include <mutex>
|
|
#include <shared_mutex>
|
|
#include <thread>
|
|
#include <type_traits>
|
|
#include <unordered_map>
|
|
|
|
#include "glog/logging.h"
|
|
|
|
namespace phi {
|
|
|
|
#ifdef __cpp_lib_void_t
|
|
using std::void_t;
|
|
#else
|
|
template <typename...>
|
|
using void_t = void;
|
|
#endif
|
|
|
|
template <typename T, typename = void>
|
|
struct IsAccumulatable : std::false_type {};
|
|
|
|
template <>
|
|
struct IsAccumulatable<std::string, void> : std::false_type {};
|
|
|
|
template <typename T>
|
|
struct IsAccumulatable<T,
|
|
void_t<decltype(std::declval<T>() += std::declval<T>())>>
|
|
: std::true_type {};
|
|
|
|
template <typename T>
|
|
class ThreadDataRegistry {
|
|
public:
|
|
// Singleton
|
|
static ThreadDataRegistry& GetInstance() {
|
|
static ThreadDataRegistry instance;
|
|
return instance;
|
|
}
|
|
|
|
T* GetMutableCurrentThreadData() { return &CurrentThreadData(); }
|
|
|
|
const T& GetCurrentThreadData() { return CurrentThreadData(); }
|
|
|
|
template <typename Alias = T,
|
|
typename = std::enable_if_t<std::is_copy_assignable<Alias>::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<std::is_copy_constructible<Alias>::value>>
|
|
std::unordered_map<uint64_t, T> GetAllThreadDataByValue() {
|
|
return impl_->GetAllThreadDataByValue();
|
|
}
|
|
|
|
// Returns current snapshot of all threads. Make sure there is no thread
|
|
// create/destroy when using it.
|
|
std::unordered_map<uint64_t, std::reference_wrapper<T>>
|
|
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<std::shared_mutex>;
|
|
// Special case : mac. https://github.com/facebook/react-native/issues/31250
|
|
#else
|
|
using LockType = std::mutex;
|
|
using SharedLockGuardType = std::lock_guard<std::mutex>;
|
|
#endif
|
|
#elif defined(_MSC_VER) // MSVC
|
|
using LockType = std::shared_mutex;
|
|
using SharedLockGuardType = std::shared_lock<std::shared_mutex>;
|
|
#else // other compilers
|
|
using LockType = std::mutex;
|
|
using SharedLockGuardType = std::lock_guard<std::mutex>;
|
|
#endif
|
|
|
|
class ThreadDataHolder;
|
|
class ThreadDataRegistryImpl {
|
|
public:
|
|
void RegisterData(uint64_t tid, ThreadDataHolder* tls_obj) {
|
|
std::lock_guard<LockType> guard(lock_);
|
|
tid_map_[tid] = tls_obj;
|
|
}
|
|
|
|
template <typename Alias = T>
|
|
void AccumulateToAnotherThread(...) {}
|
|
|
|
template <typename Alias = T,
|
|
typename = std::enable_if_t<IsAccumulatable<Alias>::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<LockType> guard(lock_);
|
|
AccumulateToAnotherThread(tid);
|
|
tid_map_.erase(tid);
|
|
}
|
|
|
|
template <
|
|
typename Alias = T,
|
|
typename = std::enable_if_t<std::is_copy_constructible<Alias>::value>>
|
|
std::unordered_map<uint64_t, T> GetAllThreadDataByValue() {
|
|
std::unordered_map<uint64_t, T> 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<uint64_t, std::reference_wrapper<T>>
|
|
GetAllThreadDataByRef() {
|
|
std::unordered_map<uint64_t, std::reference_wrapper<T>> 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<uint64_t, ThreadDataHolder*> tid_map_; // not owned
|
|
};
|
|
|
|
class ThreadDataHolder {
|
|
public:
|
|
explicit ThreadDataHolder(
|
|
std::shared_ptr<ThreadDataRegistryImpl> registry) {
|
|
registry_ = std::move(registry);
|
|
tid_ = std::hash<std::thread::id>()(std::this_thread::get_id());
|
|
registry_->RegisterData(tid_, this);
|
|
}
|
|
|
|
~ThreadDataHolder() { registry_->UnregisterData(tid_); }
|
|
|
|
T& GetData() { return data_; }
|
|
|
|
private:
|
|
std::shared_ptr<ThreadDataRegistryImpl> registry_;
|
|
uint64_t tid_;
|
|
T data_;
|
|
};
|
|
|
|
// methods
|
|
ThreadDataRegistry() { impl_ = std::make_shared<ThreadDataRegistryImpl>(); }
|
|
|
|
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<ThreadDataRegistryImpl> impl_;
|
|
};
|
|
|
|
} // namespace phi
|