blob: b8280f1d74b0c2e3ea88cd2df9ad8636273f8975 [file] [log] [blame]
Nicolas Capens0bac2852016-05-07 06:09:58 -04001// Copyright 2016 The SwiftShader Authors. All Rights Reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#ifndef sw_Thread_hpp
16#define sw_Thread_hpp
17
18#if defined(_WIN32)
19 #ifndef WIN32_LEAN_AND_MEAN
20 #define WIN32_LEAN_AND_MEAN
21 #endif
22 #include <windows.h>
23 #include <intrin.h>
24#else
25 #include <pthread.h>
26 #include <sched.h>
27 #include <unistd.h>
Nicolas Capens4d97f362017-07-11 13:49:26 -040028 #define TLS_OUT_OF_INDEXES (pthread_key_t)(~0)
Nicolas Capens0bac2852016-05-07 06:09:58 -040029#endif
30
Nicolas Capens4ad365b2017-09-12 16:39:42 -040031#include <stdlib.h>
32
Alexis Hetu6b164c32017-09-20 11:24:52 -040033#if defined(__clang__)
Cezary Kulakowskid27cdd52017-11-09 15:37:20 +010034#if __has_include(<atomic>) // clang has an explicit check for the availability of atomic
35#define USE_STD_ATOMIC 1
36#endif
Alexis Hetu6b164c32017-09-20 11:24:52 -040037// atomic is available in C++11 or newer, and in Visual Studio 2012 or newer
38#elif (defined(_MSC_VER) && (_MSC_VER >= 1700)) || (__cplusplus >= 201103L)
39#define USE_STD_ATOMIC 1
40#endif
41
42#if USE_STD_ATOMIC
43#include <atomic>
44#endif
45
Nicolas Capens0bac2852016-05-07 06:09:58 -040046namespace sw
47{
48 class Event;
49
50 class Thread
51 {
52 public:
53 Thread(void (*threadFunction)(void *parameters), void *parameters);
54
55 ~Thread();
56
57 void join();
58
59 static void yield();
60 static void sleep(int milliseconds);
61
62 #if defined(_WIN32)
63 typedef DWORD LocalStorageKey;
64 #else
65 typedef pthread_key_t LocalStorageKey;
66 #endif
67
Cody Schuffelen124c81a2019-03-15 15:21:34 -070068 static LocalStorageKey allocateLocalStorageKey(void (*destructor)(void *storage) = free);
Nicolas Capens0bac2852016-05-07 06:09:58 -040069 static void freeLocalStorageKey(LocalStorageKey key);
Nicolas Capens4ad365b2017-09-12 16:39:42 -040070 static void *allocateLocalStorage(LocalStorageKey key, size_t size);
Nicolas Capens0bac2852016-05-07 06:09:58 -040071 static void *getLocalStorage(LocalStorageKey key);
Nicolas Capens4ad365b2017-09-12 16:39:42 -040072 static void freeLocalStorage(LocalStorageKey key);
Nicolas Capens0bac2852016-05-07 06:09:58 -040073
74 private:
75 struct Entry
76 {
77 void (*const threadFunction)(void *parameters);
78 void *threadParameters;
79 Event *init;
80 };
81
82 #if defined(_WIN32)
83 static unsigned long __stdcall startFunction(void *parameters);
84 HANDLE handle;
85 #else
86 static void *startFunction(void *parameters);
87 pthread_t handle;
88 #endif
Nicolas Capens3fbe73e2017-02-02 13:54:09 -050089
90 bool hasJoined = false;
Nicolas Capens0bac2852016-05-07 06:09:58 -040091 };
92
93 class Event
94 {
95 friend class Thread;
96
97 public:
98 Event();
99
100 ~Event();
101
102 void signal();
103 void wait();
104
105 private:
106 #if defined(_WIN32)
107 HANDLE handle;
108 #else
109 pthread_cond_t handle;
110 pthread_mutex_t mutex;
111 volatile bool signaled;
112 #endif
113 };
114
115 #if PERF_PROFILE
116 int64_t atomicExchange(int64_t volatile *target, int64_t value);
Cody Schuffelen124c81a2019-03-15 15:21:34 -0700117 int atomicExchange(int volatile *target, int value);
Nicolas Capens0bac2852016-05-07 06:09:58 -0400118 #endif
119
Nicolas Capens0bac2852016-05-07 06:09:58 -0400120 int atomicIncrement(int volatile *value);
121 int atomicDecrement(int volatile *value);
122 int atomicAdd(int volatile *target, int value);
123 void nop();
124}
125
126namespace sw
127{
128 inline void Thread::yield()
129 {
130 #if defined(_WIN32)
131 Sleep(0);
132 #elif defined(__APPLE__)
133 pthread_yield_np();
134 #else
135 sched_yield();
136 #endif
137 }
138
139 inline void Thread::sleep(int milliseconds)
140 {
141 #if defined(_WIN32)
142 Sleep(milliseconds);
143 #else
144 usleep(1000 * milliseconds);
145 #endif
146 }
147
Cody Schuffelen124c81a2019-03-15 15:21:34 -0700148 inline Thread::LocalStorageKey Thread::allocateLocalStorageKey(void (*destructor)(void *storage))
Nicolas Capens0bac2852016-05-07 06:09:58 -0400149 {
150 #if defined(_WIN32)
151 return TlsAlloc();
152 #else
153 LocalStorageKey key;
Cody Schuffelen124c81a2019-03-15 15:21:34 -0700154 pthread_key_create(&key, destructor);
Nicolas Capens0bac2852016-05-07 06:09:58 -0400155 return key;
156 #endif
157 }
158
159 inline void Thread::freeLocalStorageKey(LocalStorageKey key)
160 {
161 #if defined(_WIN32)
162 TlsFree(key);
163 #else
Nicolas Capens420b64d2017-07-07 17:01:16 -0400164 pthread_key_delete(key); // Using an invalid key is an error but not undefined behavior.
Nicolas Capens0bac2852016-05-07 06:09:58 -0400165 #endif
166 }
167
Nicolas Capens4ad365b2017-09-12 16:39:42 -0400168 inline void *Thread::allocateLocalStorage(LocalStorageKey key, size_t size)
Nicolas Capens0bac2852016-05-07 06:09:58 -0400169 {
Nicolas Capens4ad365b2017-09-12 16:39:42 -0400170 if(key == TLS_OUT_OF_INDEXES)
171 {
172 return nullptr;
173 }
174
175 freeLocalStorage(key);
176
177 void *storage = malloc(size);
178
Nicolas Capens0bac2852016-05-07 06:09:58 -0400179 #if defined(_WIN32)
Nicolas Capens4ad365b2017-09-12 16:39:42 -0400180 TlsSetValue(key, storage);
Nicolas Capens0bac2852016-05-07 06:09:58 -0400181 #else
Nicolas Capens4ad365b2017-09-12 16:39:42 -0400182 pthread_setspecific(key, storage);
Nicolas Capens0bac2852016-05-07 06:09:58 -0400183 #endif
Nicolas Capens4ad365b2017-09-12 16:39:42 -0400184
185 return storage;
Nicolas Capens0bac2852016-05-07 06:09:58 -0400186 }
187
188 inline void *Thread::getLocalStorage(LocalStorageKey key)
189 {
190 #if defined(_WIN32)
191 return TlsGetValue(key);
192 #else
Nicolas Capens420b64d2017-07-07 17:01:16 -0400193 if(key == TLS_OUT_OF_INDEXES) // Avoid undefined behavior.
194 {
195 return nullptr;
196 }
197
Nicolas Capens0bac2852016-05-07 06:09:58 -0400198 return pthread_getspecific(key);
199 #endif
200 }
201
Nicolas Capens4ad365b2017-09-12 16:39:42 -0400202 inline void Thread::freeLocalStorage(LocalStorageKey key)
203 {
204 free(getLocalStorage(key));
205
206 #if defined(_WIN32)
207 TlsSetValue(key, nullptr);
208 #else
209 pthread_setspecific(key, nullptr);
210 #endif
211 }
212
Nicolas Capens0bac2852016-05-07 06:09:58 -0400213 inline void Event::signal()
214 {
215 #if defined(_WIN32)
216 SetEvent(handle);
217 #else
218 pthread_mutex_lock(&mutex);
219 signaled = true;
220 pthread_cond_signal(&handle);
221 pthread_mutex_unlock(&mutex);
222 #endif
223 }
224
225 inline void Event::wait()
226 {
227 #if defined(_WIN32)
228 WaitForSingleObject(handle, INFINITE);
229 #else
230 pthread_mutex_lock(&mutex);
231 while(!signaled) pthread_cond_wait(&handle, &mutex);
232 signaled = false;
233 pthread_mutex_unlock(&mutex);
234 #endif
235 }
236
237 #if PERF_PROFILE
238 inline int64_t atomicExchange(volatile int64_t *target, int64_t value)
239 {
240 #if defined(_WIN32)
241 return InterlockedExchange64(target, value);
242 #else
243 int ret;
Cody Schuffelen124c81a2019-03-15 15:21:34 -0700244 __asm__ __volatile__("lock; xchg8 %x0,(%x1)" : "=r" (ret) :"r" (target), "0" (value) : "memory" );
Nicolas Capens0bac2852016-05-07 06:09:58 -0400245 return ret;
246 #endif
247 }
Nicolas Capens0bac2852016-05-07 06:09:58 -0400248
249 inline int atomicExchange(volatile int *target, int value)
250 {
251 #if defined(_WIN32)
252 return InterlockedExchange((volatile long*)target, (long)value);
253 #else
254 int ret;
Cody Schuffelen124c81a2019-03-15 15:21:34 -0700255 __asm__ __volatile__("lock; xchgl %x0,(%x1)" : "=r" (ret) :"r" (target), "0" (value) : "memory" );
Nicolas Capens0bac2852016-05-07 06:09:58 -0400256 return ret;
257 #endif
258 }
Cody Schuffelen124c81a2019-03-15 15:21:34 -0700259 #endif
Nicolas Capens0bac2852016-05-07 06:09:58 -0400260
261 inline int atomicIncrement(volatile int *value)
262 {
263 #if defined(_WIN32)
264 return InterlockedIncrement((volatile long*)value);
265 #else
266 return __sync_add_and_fetch(value, 1);
267 #endif
268 }
269
270 inline int atomicDecrement(volatile int *value)
271 {
272 #if defined(_WIN32)
273 return InterlockedDecrement((volatile long*)value);
274 #else
275 return __sync_sub_and_fetch(value, 1);
276 #endif
277 }
278
279 inline int atomicAdd(volatile int* target, int value)
280 {
Alexis Hetu6b164c32017-09-20 11:24:52 -0400281 #if defined(_WIN32)
Nicolas Capens0bac2852016-05-07 06:09:58 -0400282 return InterlockedExchangeAdd((volatile long*)target, value) + value;
283 #else
284 return __sync_add_and_fetch(target, value);
285 #endif
286 }
287
288 inline void nop()
289 {
290 #if defined(_WIN32)
291 __nop();
292 #else
293 __asm__ __volatile__ ("nop");
294 #endif
295 }
Alexis Hetu6b164c32017-09-20 11:24:52 -0400296
297 #if USE_STD_ATOMIC
298 class AtomicInt
299 {
300 public:
301 AtomicInt() : ai() {}
302 AtomicInt(int i) : ai(i) {}
303
304 inline operator int() const { return ai.load(std::memory_order_acquire); }
305 inline void operator=(const AtomicInt& i) { ai.store(i.ai.load(std::memory_order_acquire), std::memory_order_release); }
306 inline void operator=(int i) { ai.store(i, std::memory_order_release); }
307 inline void operator--() { ai.fetch_sub(1, std::memory_order_acq_rel); }
308 inline void operator++() { ai.fetch_add(1, std::memory_order_acq_rel); }
309 inline int operator--(int) { return ai.fetch_sub(1, std::memory_order_acq_rel) - 1; }
310 inline int operator++(int) { return ai.fetch_add(1, std::memory_order_acq_rel) + 1; }
311 inline void operator-=(int i) { ai.fetch_sub(i, std::memory_order_acq_rel); }
312 inline void operator+=(int i) { ai.fetch_add(i, std::memory_order_acq_rel); }
313 private:
314 std::atomic<int> ai;
315 };
316 #else
317 class AtomicInt
318 {
319 public:
320 AtomicInt() {}
321 AtomicInt(int i) : vi(i) {}
322
323 inline operator int() const { return vi; } // Note: this isn't a guaranteed atomic operation
324 inline void operator=(const AtomicInt& i) { sw::atomicExchange(&vi, i.vi); }
325 inline void operator=(int i) { sw::atomicExchange(&vi, i); }
326 inline void operator--() { sw::atomicDecrement(&vi); }
327 inline void operator++() { sw::atomicIncrement(&vi); }
328 inline int operator--(int) { return sw::atomicDecrement(&vi); }
329 inline int operator++(int) { return sw::atomicIncrement(&vi); }
330 inline void operator-=(int i) { sw::atomicAdd(&vi, -i); }
331 inline void operator+=(int i) { sw::atomicAdd(&vi, i); }
332 private:
333 volatile int vi;
334 };
335 #endif
Nicolas Capens0bac2852016-05-07 06:09:58 -0400336}
337
338#endif // sw_Thread_hpp