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