blob: ed3bb5509cfde9e46b71d727fdc49e850a056a5a [file] [log] [blame]
Nicolas Capensc07dc4b2018-08-06 14:20:45 -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 rr_Thread_hpp
16#define rr_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>
28 #define TLS_OUT_OF_INDEXES (pthread_key_t)(~0)
29#endif
30
31#include <stdlib.h>
32
33#if defined(__clang__)
34#if __has_include(<atomic>) // clang has an explicit check for the availability of atomic
35#define USE_STD_ATOMIC 1
36#endif
37// 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
46namespace rr
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
68 static LocalStorageKey allocateLocalStorageKey(void (*destructor)(void *storage) = free);
69 static void freeLocalStorageKey(LocalStorageKey key);
70 static void *allocateLocalStorage(LocalStorageKey key, size_t size);
71 static void *getLocalStorage(LocalStorageKey key);
72 static void freeLocalStorage(LocalStorageKey key);
73
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
89
90 bool hasJoined = false;
91 };
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);
Nicolas Capens758796a2018-10-12 16:45:51 -0400117 int atomicExchange(int volatile *target, int value);
Nicolas Capensc07dc4b2018-08-06 14:20:45 -0400118 #endif
119
Nicolas Capensc07dc4b2018-08-06 14:20:45 -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 rr
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
148 inline Thread::LocalStorageKey Thread::allocateLocalStorageKey(void (*destructor)(void *storage))
149 {
150 #if defined(_WIN32)
151 return TlsAlloc();
152 #else
153 LocalStorageKey key;
154 pthread_key_create(&key, destructor);
155 return key;
156 #endif
157 }
158
159 inline void Thread::freeLocalStorageKey(LocalStorageKey key)
160 {
161 #if defined(_WIN32)
162 TlsFree(key);
163 #else
164 pthread_key_delete(key); // Using an invalid key is an error but not undefined behavior.
165 #endif
166 }
167
168 inline void *Thread::allocateLocalStorage(LocalStorageKey key, size_t size)
169 {
170 if(key == TLS_OUT_OF_INDEXES)
171 {
172 return nullptr;
173 }
174
175 freeLocalStorage(key);
176
177 void *storage = malloc(size);
178
179 #if defined(_WIN32)
180 TlsSetValue(key, storage);
181 #else
182 pthread_setspecific(key, storage);
183 #endif
184
185 return storage;
186 }
187
188 inline void *Thread::getLocalStorage(LocalStorageKey key)
189 {
190 #if defined(_WIN32)
191 return TlsGetValue(key);
192 #else
193 if(key == TLS_OUT_OF_INDEXES) // Avoid undefined behavior.
194 {
195 return nullptr;
196 }
197
198 return pthread_getspecific(key);
199 #endif
200 }
201
202 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
213 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;
Nicolas Capens758796a2018-10-12 16:45:51 -0400244 __asm__ __volatile__("lock; xchg8 %x0,(%x1)" : "=r" (ret) :"r" (target), "0" (value) : "memory" );
Nicolas Capensc07dc4b2018-08-06 14:20:45 -0400245 return ret;
246 #endif
247 }
Nicolas Capensc07dc4b2018-08-06 14:20:45 -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;
Nicolas Capens758796a2018-10-12 16:45:51 -0400255 __asm__ __volatile__("lock; xchgl %x0,(%x1)" : "=r" (ret) :"r" (target), "0" (value) : "memory" );
Nicolas Capensc07dc4b2018-08-06 14:20:45 -0400256 return ret;
257 #endif
258 }
Nicolas Capens758796a2018-10-12 16:45:51 -0400259 #endif
Nicolas Capensc07dc4b2018-08-06 14:20:45 -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 {
281 #if defined(_WIN32)
282 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 }
296
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) { atomicExchange(&vi, i.vi); }
325 inline void operator=(int i) { atomicExchange(&vi, i); }
326 inline void operator--() { atomicDecrement(&vi); }
327 inline void operator++() { atomicIncrement(&vi); }
328 inline int operator--(int) { return atomicDecrement(&vi); }
329 inline int operator++(int) { return atomicIncrement(&vi); }
330 inline void operator-=(int i) { atomicAdd(&vi, -i); }
331 inline void operator+=(int i) { atomicAdd(&vi, i); }
332 private:
333 volatile int vi;
334 };
335 #endif
336}
337
338#endif // rr_Thread_hpp