FastLED 3.10.6
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_NO_EXCEPT : 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) FL_NO_EXCEPT
44 : mDefaultValue(defaultVal), mHasDefault(true) {
45 initializeKey();
46 }
47
48 // Destructor - cleanup pthread key
49 ~ThreadLocalReal() FL_NO_EXCEPT {
50 if (mKeyInitialized) {
51 // Manually clean up storage for the current thread.
52 // pthread_key_delete does NOT call the cleanup function - it only
53 // removes the key. Thread-specific data must be freed manually.
54 ThreadStorage* storage = getStorage();
55 if (storage) {
56 pthread_setspecific(mKey, nullptr);
57 delete storage; // ok bare allocation
58 }
59 pthread_key_delete(mKey);
60 }
61 }
62
63 // Copy constructor
64 ThreadLocalReal(const ThreadLocalReal& other) FL_NO_EXCEPT : mDefaultValue(other.mDefaultValue), mHasDefault(other.mHasDefault) {
65 initializeKey();
66 }
67
68 // Assignment operator
69 ThreadLocalReal& operator=(const ThreadLocalReal& other) FL_NO_EXCEPT {
70 if (this != &other) {
71 mDefaultValue = other.mDefaultValue;
72 mHasDefault = other.mHasDefault;
73 // Key remains the same for this instance
74 }
75 return *this;
76 }
77
78 // Access the thread-local instance
79 T &access() FL_NO_EXCEPT {
80 ThreadStorage* storage = getStorage();
81 if (!storage) {
82 storage = createStorage();
83 }
84 if (mHasDefault && !storage->initialized) {
85 copyValue(storage->value, mDefaultValue);
86 storage->initialized = true;
87 }
88 return storage->value;
89 }
90
91 const T &access() const FL_NO_EXCEPT {
92 ThreadStorage* storage = getStorage();
93 if (!storage) {
94 storage = createStorage();
95 }
96 if (mHasDefault && !storage->initialized) {
97 copyValue(storage->value, mDefaultValue);
98 storage->initialized = true;
99 }
100 return storage->value;
101 }
102
103 // Set the value for this thread
104 void set(const T& value) FL_NO_EXCEPT {
105 copyValue(access(), value);
106 }
107
108 // Convenience operators
109 operator T &() FL_NO_EXCEPT { return access(); }
110 operator const T &() const FL_NO_EXCEPT { return access(); }
111
112 ThreadLocalReal &operator=(const T &v) FL_NO_EXCEPT {
113 set(v);
114 return *this;
115 }
116
117 private:
118 // Helper function to copy values, specialized for arrays
119 template<typename U>
120 static void copyValue(U& dest, const U& src) FL_NO_EXCEPT {
121 dest = src; // Default behavior for non-array types
122 }
123
124 // Specialization for array types
125 template<typename U, size_t N>
126 static void copyValue(U (&dest)[N], const U (&src)[N]) FL_NO_EXCEPT {
127 for (size_t i = 0; i < N; ++i) {
128 copyValue(dest[i], src[i]); // Recursively handle nested arrays
129 }
130 }
131
132 // Storage for thread-local data
133 struct ThreadStorage {
134 T value{};
135 bool initialized = false;
136 };
137
138 // POSIX thread key for this instance
139 pthread_key_t mKey;
140 bool mKeyInitialized = false;
141 T mDefaultValue{};
142 bool mHasDefault = false;
143
144 // Initialize the pthread key
145 void initializeKey() FL_NO_EXCEPT {
146 int result = pthread_key_create(&mKey, cleanupThreadStorage);
147 if (result == 0) {
148 mKeyInitialized = true;
149 } else {
150 // Handle error - for now just mark as not initialized
151 mKeyInitialized = false;
152 }
153 }
154
155 // Get thread-specific storage
156 ThreadStorage* getStorage() const FL_NO_EXCEPT {
157 if (!mKeyInitialized) {
158 return nullptr;
159 }
160 return static_cast<ThreadStorage*>(pthread_getspecific(mKey));
161 }
162
163 // Create thread-specific storage
164 ThreadStorage* createStorage() const FL_NO_EXCEPT {
165 if (!mKeyInitialized) {
166 return nullptr;
167 }
168
169 ThreadStorage* storage = new ThreadStorage(); // ok bare allocation
170 int result = pthread_setspecific(mKey, storage);
171 if (result != 0) {
172 delete storage; // ok bare allocation
173 return nullptr;
174 }
175 return storage;
176 }
177
178 // Cleanup function called when thread exits
179 static void cleanupThreadStorage(void* data) FL_NO_EXCEPT {
180 if (data) {
181 delete static_cast<ThreadStorage*>(data); // ok bare allocation
182 }
183 }
184};
185
186#endif // FASTLED_USE_THREAD_LOCAL
187
189
190template <typename T> class ThreadLocalFake {
191 public:
192 // Default: each thread's object is default-constructed
194
195 // With default: each thread's object is copy-constructed from defaultVal
196 template <typename U>
197 explicit ThreadLocalFake(const U &defaultVal) FL_NO_EXCEPT : mValue(defaultVal) {}
198
199 // Access the thread-local instance (not actually thread-local in fake version)
200 T &access() FL_NO_EXCEPT { return mValue; }
201 const T &access() const FL_NO_EXCEPT { return mValue; }
202
203 // Set the value (globally shared in fake version)
204 void set(const T& value) FL_NO_EXCEPT {
205 mValue = value;
206 }
207
208 // Convenience operators for "ThreadLocal<T> = x;"
209 operator T &() FL_NO_EXCEPT { return access(); }
210 operator const T &() const FL_NO_EXCEPT { return access(); }
211
213 set(v);
214 return *this;
215 }
216
217 private:
219};
220
221} // namespace fl
const T & access() const FL_NO_EXCEPT
void set(const T &value) FL_NO_EXCEPT
ThreadLocalFake & operator=(const T &v) FL_NO_EXCEPT
ThreadLocalFake() FL_NO_EXCEPT
T & access() FL_NO_EXCEPT
ThreadLocalFake(const U &defaultVal) FL_NO_EXCEPT
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
InputGamut g FL_NO_EXCEPT
Definition rgbw.h:121
Base definition for an LED controller.
Definition crgb.hpp:179