File indexing completed on 2025-12-16 10:14:27
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010 #ifndef EIGEN_CXX11_THREADPOOL_THREAD_LOCAL_H
0011 #define EIGEN_CXX11_THREADPOOL_THREAD_LOCAL_H
0012
0013 #ifdef EIGEN_AVOID_THREAD_LOCAL
0014
0015 #ifdef EIGEN_THREAD_LOCAL
0016 #undef EIGEN_THREAD_LOCAL
0017 #endif
0018
0019 #else
0020
0021 #if EIGEN_MAX_CPP_VER >= 11 && \
0022 ((EIGEN_COMP_GNUC && EIGEN_GNUC_AT_LEAST(4, 8)) || \
0023 __has_feature(cxx_thread_local) || \
0024 (EIGEN_COMP_MSVC >= 1900) )
0025 #define EIGEN_THREAD_LOCAL static thread_local
0026 #endif
0027
0028
0029 #if defined(__APPLE__)
0030
0031
0032 #include <Availability.h>
0033 #include <TargetConditionals.h>
0034 #endif
0035
0036
0037 #if defined(__apple_build_version__) && \
0038 ((__apple_build_version__ < 8000042) || \
0039 (TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_9_0))
0040
0041
0042 #undef EIGEN_THREAD_LOCAL
0043
0044 #elif defined(__ANDROID__) && EIGEN_COMP_CLANG
0045
0046
0047
0048
0049
0050
0051
0052
0053 #if __has_include(<android/ndk-version.h>)
0054 #include <android/ndk-version.h>
0055 #endif
0056 #if defined(__ANDROID__) && defined(__clang__) && defined(__NDK_MAJOR__) && \
0057 defined(__NDK_MINOR__) && \
0058 ((__NDK_MAJOR__ < 12) || ((__NDK_MAJOR__ == 12) && (__NDK_MINOR__ < 1)))
0059 #undef EIGEN_THREAD_LOCAL
0060 #endif
0061 #endif
0062
0063 #endif
0064
0065 namespace Eigen {
0066
0067 namespace internal {
0068 template <typename T>
0069 struct ThreadLocalNoOpInitialize {
0070 void operator()(T&) const {}
0071 };
0072
0073 template <typename T>
0074 struct ThreadLocalNoOpRelease {
0075 void operator()(T&) const {}
0076 };
0077
0078 }
0079
0080
0081
0082
0083
0084
0085
0086
0087
0088
0089
0090
0091
0092
0093
0094
0095
0096
0097
0098
0099
0100
0101
0102
0103
0104
0105
0106
0107
0108
0109
0110
0111
0112 template <typename T,
0113 typename Initialize = internal::ThreadLocalNoOpInitialize<T>,
0114 typename Release = internal::ThreadLocalNoOpRelease<T>>
0115 class ThreadLocal {
0116
0117 static_assert(std::is_default_constructible<T>::value,
0118 "ThreadLocal data type must be default constructible");
0119
0120 public:
0121 explicit ThreadLocal(int capacity)
0122 : ThreadLocal(capacity, internal::ThreadLocalNoOpInitialize<T>(),
0123 internal::ThreadLocalNoOpRelease<T>()) {}
0124
0125 ThreadLocal(int capacity, Initialize initialize)
0126 : ThreadLocal(capacity, std::move(initialize),
0127 internal::ThreadLocalNoOpRelease<T>()) {}
0128
0129 ThreadLocal(int capacity, Initialize initialize, Release release)
0130 : initialize_(std::move(initialize)),
0131 release_(std::move(release)),
0132 capacity_(capacity),
0133 data_(capacity_),
0134 ptr_(capacity_),
0135 filled_records_(0) {
0136 eigen_assert(capacity_ >= 0);
0137 data_.resize(capacity_);
0138 for (int i = 0; i < capacity_; ++i) {
0139 ptr_.emplace_back(nullptr);
0140 }
0141 }
0142
0143 T& local() {
0144 std::thread::id this_thread = std::this_thread::get_id();
0145 if (capacity_ == 0) return SpilledLocal(this_thread);
0146
0147 std::size_t h = std::hash<std::thread::id>()(this_thread);
0148 const int start_idx = h % capacity_;
0149
0150
0151
0152
0153
0154
0155
0156
0157
0158 int idx = start_idx;
0159 while (ptr_[idx].load() != nullptr) {
0160 ThreadIdAndValue& record = *(ptr_[idx].load());
0161 if (record.thread_id == this_thread) return record.value;
0162
0163 idx += 1;
0164 if (idx >= capacity_) idx -= capacity_;
0165 if (idx == start_idx) break;
0166 }
0167
0168
0169
0170
0171
0172 if (filled_records_.load() >= capacity_) return SpilledLocal(this_thread);
0173
0174
0175
0176
0177
0178 int insertion_index =
0179 filled_records_.fetch_add(1, std::memory_order_relaxed);
0180 if (insertion_index >= capacity_) return SpilledLocal(this_thread);
0181
0182
0183
0184 data_[insertion_index].thread_id = this_thread;
0185 initialize_(data_[insertion_index].value);
0186
0187
0188 ThreadIdAndValue* inserted = &data_[insertion_index];
0189
0190
0191 ThreadIdAndValue* empty = nullptr;
0192
0193
0194
0195
0196
0197 const int insertion_idx = idx;
0198
0199 do {
0200
0201 idx = insertion_idx;
0202 while (ptr_[idx].load() != nullptr) {
0203 idx += 1;
0204 if (idx >= capacity_) idx -= capacity_;
0205
0206
0207 eigen_assert(idx != insertion_idx);
0208 }
0209
0210
0211 } while (!ptr_[idx].compare_exchange_weak(empty, inserted));
0212
0213 return inserted->value;
0214 }
0215
0216
0217 void ForEach(std::function<void(std::thread::id, T&)> f) {
0218
0219
0220 for (auto& ptr : ptr_) {
0221 ThreadIdAndValue* record = ptr.load();
0222 if (record == nullptr) continue;
0223 f(record->thread_id, record->value);
0224 }
0225
0226
0227 if (filled_records_.load(std::memory_order_relaxed) < capacity_) return;
0228
0229
0230 std::unique_lock<std::mutex> lock(mu_);
0231 for (auto& kv : per_thread_map_) {
0232 f(kv.first, kv.second);
0233 }
0234 }
0235
0236
0237 ~ThreadLocal() {
0238
0239
0240 for (auto& ptr : ptr_) {
0241 ThreadIdAndValue* record = ptr.load();
0242 if (record == nullptr) continue;
0243 release_(record->value);
0244 }
0245
0246
0247 if (filled_records_.load(std::memory_order_relaxed) < capacity_) return;
0248
0249
0250 std::unique_lock<std::mutex> lock(mu_);
0251 for (auto& kv : per_thread_map_) {
0252 release_(kv.second);
0253 }
0254 }
0255
0256 private:
0257 struct ThreadIdAndValue {
0258 std::thread::id thread_id;
0259 T value;
0260 };
0261
0262
0263 T& SpilledLocal(std::thread::id this_thread) {
0264 std::unique_lock<std::mutex> lock(mu_);
0265
0266 auto it = per_thread_map_.find(this_thread);
0267 if (it == per_thread_map_.end()) {
0268 auto result = per_thread_map_.emplace(this_thread, T());
0269 eigen_assert(result.second);
0270 initialize_((*result.first).second);
0271 return (*result.first).second;
0272 } else {
0273 return it->second;
0274 }
0275 }
0276
0277 Initialize initialize_;
0278 Release release_;
0279 const int capacity_;
0280
0281
0282
0283 MaxSizeVector<ThreadIdAndValue> data_;
0284
0285
0286
0287 MaxSizeVector<std::atomic<ThreadIdAndValue*>> ptr_;
0288
0289
0290 std::atomic<int> filled_records_;
0291
0292
0293
0294
0295 std::mutex mu_;
0296 std::unordered_map<std::thread::id, T> per_thread_map_;
0297 };
0298
0299 }
0300
0301 #endif