11#ifndef EIGEN_THREADPOOL_THREAD_LOCAL_H
12#define EIGEN_THREADPOOL_THREAD_LOCAL_H
14#ifdef EIGEN_AVOID_THREAD_LOCAL
16#ifdef EIGEN_THREAD_LOCAL
17#undef EIGEN_THREAD_LOCAL
22#define EIGEN_THREAD_LOCAL static thread_local
28#include <Availability.h>
29#include <TargetConditionals.h>
32#if EIGEN_COMP_CLANGAPPLE && TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_9_0
34#undef EIGEN_THREAD_LOCAL
40#include "./InternalHeaderCheck.h"
46struct ThreadLocalNoOpInitialize {
47 void operator()(T&)
const {}
51struct ThreadLocalNoOpRelease {
52 void operator()(T&)
const {}
89template <
typename T,
typename Initialize =
internal::ThreadLocalNoOpInitialize<T>,
90 typename Release =
internal::ThreadLocalNoOpRelease<T>>
93 static_assert(std::is_default_constructible<T>::value,
"ThreadLocal data type must be default constructible");
96 explicit ThreadLocal(
int capacity)
97 : ThreadLocal(capacity, internal::ThreadLocalNoOpInitialize<T>(), internal::ThreadLocalNoOpRelease<T>()) {}
99 ThreadLocal(
int capacity, Initialize initialize)
100 : ThreadLocal(capacity, std::move(initialize), internal::ThreadLocalNoOpRelease<T>()) {}
102 ThreadLocal(
int capacity, Initialize initialize, Release release)
103 : initialize_(std::move(initialize)),
104 release_(std::move(release)),
109 eigen_assert(capacity_ >= 0);
110 data_.resize(capacity_);
111 for (
int i = 0; i < capacity_; ++i) {
112 ptr_.emplace_back(
nullptr);
117 std::thread::id this_thread = std::this_thread::get_id();
118 if (capacity_ == 0)
return SpilledLocal(this_thread);
120 std::size_t h = std::hash<std::thread::id>()(this_thread);
121 const int start_idx = h % capacity_;
132 while (ptr_[idx].load() !=
nullptr) {
133 ThreadIdAndValue& record = *(ptr_[idx].load());
134 if (record.thread_id == this_thread)
return record.value;
137 if (idx >= capacity_) idx -= capacity_;
138 if (idx == start_idx)
break;
145 if (filled_records_.load() >= capacity_)
return SpilledLocal(this_thread);
151 int insertion_index = filled_records_.fetch_add(1, std::memory_order_relaxed);
152 if (insertion_index >= capacity_)
return SpilledLocal(this_thread);
156 data_[insertion_index].thread_id = this_thread;
157 initialize_(data_[insertion_index].value);
160 ThreadIdAndValue* inserted = &data_[insertion_index];
163 ThreadIdAndValue* empty =
nullptr;
169 const int insertion_idx = idx;
174 while (ptr_[idx].load() !=
nullptr) {
176 if (idx >= capacity_) idx -= capacity_;
179 eigen_assert(idx != insertion_idx);
183 }
while (!ptr_[idx].compare_exchange_weak(empty, inserted));
185 return inserted->value;
189 void ForEach(std::function<
void(std::thread::id, T&)> f) {
192 for (
auto& ptr : ptr_) {
193 ThreadIdAndValue* record = ptr.load();
194 if (record ==
nullptr)
continue;
195 f(record->thread_id, record->value);
199 if (filled_records_.load(std::memory_order_relaxed) < capacity_)
return;
202 EIGEN_MUTEX_LOCK lock(mu_);
203 for (
auto& kv : per_thread_map_) {
204 f(kv.first, kv.second);
212 for (
auto& ptr : ptr_) {
213 ThreadIdAndValue* record = ptr.load();
214 if (record ==
nullptr)
continue;
215 release_(record->value);
219 if (filled_records_.load(std::memory_order_relaxed) < capacity_)
return;
222 EIGEN_MUTEX_LOCK lock(mu_);
223 for (
auto& kv : per_thread_map_) {
229 struct ThreadIdAndValue {
230 std::thread::id thread_id;
235 T& SpilledLocal(std::thread::id this_thread) {
236 EIGEN_MUTEX_LOCK lock(mu_);
238 auto it = per_thread_map_.find(this_thread);
239 if (it == per_thread_map_.end()) {
240 auto result = per_thread_map_.emplace(this_thread, T());
241 eigen_assert(result.second);
242 initialize_((*result.first).second);
243 return (*result.first).second;
249 Initialize initialize_;
255 MaxSizeVector<ThreadIdAndValue> data_;
259 MaxSizeVector<std::atomic<ThreadIdAndValue*>> ptr_;
262 std::atomic<int> filled_records_;
268 std::unordered_map<std::thread::id, T> per_thread_map_;