1 // 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> 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 46 namespace 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 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); 117 int atomicExchange(int volatile *target, int value); 118 #endif 119 120 int atomicIncrement(int volatile *value); 121 int atomicDecrement(int volatile *value); 122 int atomicAdd(int volatile *target, int value); 123 void nop(); 124 } 125 126 namespace sw 127 { yield()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 sleep(int milliseconds)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 allocateLocalStorageKey(void (* destructor)(void * storage))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 freeLocalStorageKey(LocalStorageKey key)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 allocateLocalStorage(LocalStorageKey key,size_t size)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 getLocalStorage(LocalStorageKey key)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 freeLocalStorage(LocalStorageKey key)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 signal()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 wait()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 atomicExchange(volatile int64_t * target,int64_t value)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; 244 __asm__ __volatile__("lock; xchg8 %x0,(%x1)" : "=r" (ret) :"r" (target), "0" (value) : "memory" ); 245 return ret; 246 #endif 247 } 248 atomicExchange(volatile int * target,int value)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; 255 __asm__ __volatile__("lock; xchgl %x0,(%x1)" : "=r" (ret) :"r" (target), "0" (value) : "memory" ); 256 return ret; 257 #endif 258 } 259 #endif 260 atomicIncrement(volatile int * value)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 atomicDecrement(volatile int * value)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 atomicAdd(volatile int * target,int value)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 nop()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: AtomicInt()301 AtomicInt() : ai() {} AtomicInt(int i)302 AtomicInt(int i) : ai(i) {} 303 operator int() const304 inline operator int() const { return ai.load(std::memory_order_acquire); } operator =(const AtomicInt & i)305 inline void operator=(const AtomicInt& i) { ai.store(i.ai.load(std::memory_order_acquire), std::memory_order_release); } operator =(int i)306 inline void operator=(int i) { ai.store(i, std::memory_order_release); } operator --()307 inline void operator--() { ai.fetch_sub(1, std::memory_order_acq_rel); } operator ++()308 inline void operator++() { ai.fetch_add(1, std::memory_order_acq_rel); } operator --(int)309 inline int operator--(int) { return ai.fetch_sub(1, std::memory_order_acq_rel) - 1; } operator ++(int)310 inline int operator++(int) { return ai.fetch_add(1, std::memory_order_acq_rel) + 1; } operator -=(int i)311 inline void operator-=(int i) { ai.fetch_sub(i, std::memory_order_acq_rel); } operator +=(int i)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: AtomicInt()320 AtomicInt() {} AtomicInt(int i)321 AtomicInt(int i) : vi(i) {} 322 operator int() const323 inline operator int() const { return vi; } // Note: this isn't a guaranteed atomic operation operator =(const AtomicInt & i)324 inline void operator=(const AtomicInt& i) { sw::atomicExchange(&vi, i.vi); } operator =(int i)325 inline void operator=(int i) { sw::atomicExchange(&vi, i); } operator --()326 inline void operator--() { sw::atomicDecrement(&vi); } operator ++()327 inline void operator++() { sw::atomicIncrement(&vi); } operator --(int)328 inline int operator--(int) { return sw::atomicDecrement(&vi); } operator ++(int)329 inline int operator++(int) { return sw::atomicIncrement(&vi); } operator -=(int i)330 inline void operator-=(int i) { sw::atomicAdd(&vi, -i); } operator +=(int i)331 inline void operator+=(int i) { sw::atomicAdd(&vi, i); } 332 private: 333 volatile int vi; 334 }; 335 #endif 336 } 337 338 #endif // sw_Thread_hpp 339