FastLED 3.9.15
Loading...
Searching...
No Matches
thread_local.h
Go to the documentation of this file.
1#pragma once
2
3#include "fl/stl/thread.h"
4#if FASTLED_USE_THREAD_LOCAL
5// IWYU pragma: begin_keep
6#include <pthread.h> // ok include
7#include "fl/stl/noexcept.h"
8// IWYU pragma: end_keep
9#endif
10
11#if FASTLED_USE_THREAD_LOCAL
12// Real thread-local implementation using POSIX threading
13#else
14// Fake thread-local implementation (globally shared data)
15#if FASTLED_MULTITHREADED
16#warning \
17 "ThreadLocal is not implemented, using the fake version with globally shared data"
18#endif
19#endif
20
21namespace fl {
22
23#if FASTLED_USE_THREAD_LOCAL
24template <typename T> class ThreadLocalReal;
25template <typename T> using ThreadLocal = ThreadLocalReal<T>;
26#else
27template <typename T> class ThreadLocalFake;
28template <typename T> using ThreadLocal = ThreadLocalFake<T>;
29#endif
30
32
33#if FASTLED_USE_THREAD_LOCAL
34template <typename T> class ThreadLocalReal {
35 public:
36 // Default: each thread's object is default-constructed
37 ThreadLocalReal() FL_NOEXCEPT : mDefaultValue(), mHasDefault(false) {
38 initializeKey();
39 }
40
41 // With default: each thread's object is copy-constructed from defaultVal
42 template <typename U>
43 explicit ThreadLocalReal(const U &defaultVal) : mDefaultValue(defaultVal), mHasDefault(true) {
44 initializeKey();
45 }
46
47 // Destructor - cleanup pthread key
48 ~ThreadLocalReal() FL_NOEXCEPT {
49 if (mKeyInitialized) {
50 // Manually clean up storage for the current thread.
51 // pthread_key_delete does NOT call the cleanup function - it only
52 // removes the key. Thread-specific data must be freed manually.
53 ThreadStorage* storage = getStorage();
54 if (storage) {
55 pthread_setspecific(mKey, nullptr);
56 delete storage; // ok bare allocation
57 }
58 pthread_key_delete(mKey);
59 }
60 }
61
62 // Copy constructor
63 ThreadLocalReal(const ThreadLocalReal& other) FL_NOEXCEPT : mDefaultValue(other.mDefaultValue), mHasDefault(other.mHasDefault) {
64 initializeKey();
65 }
66
67 // Assignment operator
68 ThreadLocalReal& operator=(const ThreadLocalReal& other) FL_NOEXCEPT {
69 if (this != &other) {
70 mDefaultValue = other.mDefaultValue;
71 mHasDefault = other.mHasDefault;
72 // Key remains the same for this instance
73 }
74 return *this;
75 }
76
77 // Access the thread-local instance
78 T &access() {
79 ThreadStorage* storage = getStorage();
80 if (!storage) {
81 storage = createStorage();
82 }
83 if (mHasDefault && !storage->initialized) {
84 copyValue(storage->value, mDefaultValue);
85 storage->initialized = true;
86 }
87 return storage->value;
88 }
89
90 const T &access() const {
91 ThreadStorage* storage = getStorage();
92 if (!storage) {
93 storage = createStorage();
94 }
95 if (mHasDefault && !storage->initialized) {
96 copyValue(storage->value, mDefaultValue);
97 storage->initialized = true;
98 }
99 return storage->value;
100 }
101
102 // Set the value for this thread
103 void set(const T& value) {
104 copyValue(access(), value);
105 }
106
107 // Convenience operators
108 operator T &() { return access(); }
109 operator const T &() const { return access(); }
110
111 ThreadLocalReal &operator=(const T &v) FL_NOEXCEPT {
112 set(v);
113 return *this;
114 }
115
116 private:
117 // Helper function to copy values, specialized for arrays
118 template<typename U>
119 static void copyValue(U& dest, const U& src) {
120 dest = src; // Default behavior for non-array types
121 }
122
123 // Specialization for array types
124 template<typename U, size_t N>
125 static void copyValue(U (&dest)[N], const U (&src)[N]) {
126 for (size_t i = 0; i < N; ++i) {
127 copyValue(dest[i], src[i]); // Recursively handle nested arrays
128 }
129 }
130
131 // Storage for thread-local data
132 struct ThreadStorage {
133 T value{};
134 bool initialized = false;
135 };
136
137 // POSIX thread key for this instance
138 pthread_key_t mKey;
139 bool mKeyInitialized = false;
140 T mDefaultValue{};
141 bool mHasDefault = false;
142
143 // Initialize the pthread key
144 void initializeKey() {
145 int result = pthread_key_create(&mKey, cleanupThreadStorage);
146 if (result == 0) {
147 mKeyInitialized = true;
148 } else {
149 // Handle error - for now just mark as not initialized
150 mKeyInitialized = false;
151 }
152 }
153
154 // Get thread-specific storage
155 ThreadStorage* getStorage() const {
156 if (!mKeyInitialized) {
157 return nullptr;
158 }
159 return static_cast<ThreadStorage*>(pthread_getspecific(mKey));
160 }
161
162 // Create thread-specific storage
163 ThreadStorage* createStorage() const {
164 if (!mKeyInitialized) {
165 return nullptr;
166 }
167
168 ThreadStorage* storage = new ThreadStorage(); // ok bare allocation
169 int result = pthread_setspecific(mKey, storage);
170 if (result != 0) {
171 delete storage; // ok bare allocation
172 return nullptr;
173 }
174 return storage;
175 }
176
177 // Cleanup function called when thread exits
178 static void cleanupThreadStorage(void* data) {
179 if (data) {
180 delete static_cast<ThreadStorage*>(data); // ok bare allocation
181 }
182 }
183};
184
185#endif // FASTLED_USE_THREAD_LOCAL
186
188
189template <typename T> class ThreadLocalFake {
190 public:
191 // Default: each thread's object is default-constructed
193
194 // With default: each thread's object is copy-constructed from defaultVal
195 template <typename U>
196 explicit ThreadLocalFake(const U &defaultVal) FL_NOEXCEPT : mValue(defaultVal) {}
197
198 // Access the thread-local instance (not actually thread-local in fake version)
199 T &access() FL_NOEXCEPT { return mValue; }
200 const T &access() const FL_NOEXCEPT { return mValue; }
201
202 // Set the value (globally shared in fake version)
203 void set(const T& value) FL_NOEXCEPT {
204 mValue = value;
205 }
206
207 // Convenience operators for "ThreadLocal<T> = x;"
208 operator T &() FL_NOEXCEPT { return access(); }
209 operator const T &() const FL_NOEXCEPT { return access(); }
210
212 set(v);
213 return *this;
214 }
215
216 private:
218};
219
220} // namespace fl
void set(const T &value) FL_NOEXCEPT
ThreadLocalFake() FL_NOEXCEPT
ThreadLocalFake(const U &defaultVal) FL_NOEXCEPT
ThreadLocalFake & operator=(const T &v) FL_NOEXCEPT
const T & access() const FL_NOEXCEPT
T & access() FL_NOEXCEPT
Definition set.h:367
constexpr int type_rank< T >::value
ThreadLocalFake< T > ThreadLocal
expected< T, E > result
Alias for expected (Rust-style naming)
Definition result.h:31
Base definition for an LED controller.
Definition crgb.hpp:179
#define FL_NOEXCEPT