|  | // Copyright 2016 The SwiftShader Authors. All Rights Reserved. | 
|  | // | 
|  | // Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | // you may not use this file except in compliance with the License. | 
|  | // You may obtain a copy of the License at | 
|  | // | 
|  | //    http://www.apache.org/licenses/LICENSE-2.0 | 
|  | // | 
|  | // Unless required by applicable law or agreed to in writing, software | 
|  | // distributed under the License is distributed on an "AS IS" BASIS, | 
|  | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | // See the License for the specific language governing permissions and | 
|  | // limitations under the License. | 
|  |  | 
|  | #ifndef rr_Thread_hpp | 
|  | #define rr_Thread_hpp | 
|  |  | 
|  | #if defined(_WIN32) | 
|  | #ifndef WIN32_LEAN_AND_MEAN | 
|  | #define WIN32_LEAN_AND_MEAN | 
|  | #endif | 
|  | #include <windows.h> | 
|  | #include <intrin.h> | 
|  | #else | 
|  | #include <pthread.h> | 
|  | #include <sched.h> | 
|  | #include <unistd.h> | 
|  | #define TLS_OUT_OF_INDEXES (pthread_key_t)(~0) | 
|  | #endif | 
|  |  | 
|  | #include <stdlib.h> | 
|  |  | 
|  | #if defined(__clang__) | 
|  | #if __has_include(<atomic>) // clang has an explicit check for the availability of atomic | 
|  | #define USE_STD_ATOMIC 1 | 
|  | #endif | 
|  | // atomic is available in C++11 or newer, and in Visual Studio 2012 or newer | 
|  | #elif (defined(_MSC_VER) && (_MSC_VER >= 1700)) || (__cplusplus >= 201103L) | 
|  | #define USE_STD_ATOMIC 1 | 
|  | #endif | 
|  |  | 
|  | #if USE_STD_ATOMIC | 
|  | #include <atomic> | 
|  | #endif | 
|  |  | 
|  | namespace rr | 
|  | { | 
|  | class Event; | 
|  |  | 
|  | class Thread | 
|  | { | 
|  | public: | 
|  | Thread(void (*threadFunction)(void *parameters), void *parameters); | 
|  |  | 
|  | ~Thread(); | 
|  |  | 
|  | void join(); | 
|  |  | 
|  | static void yield(); | 
|  | static void sleep(int milliseconds); | 
|  |  | 
|  | #if defined(_WIN32) | 
|  | typedef DWORD LocalStorageKey; | 
|  | #else | 
|  | typedef pthread_key_t LocalStorageKey; | 
|  | #endif | 
|  |  | 
|  | static LocalStorageKey allocateLocalStorageKey(void (*destructor)(void *storage) = free); | 
|  | static void freeLocalStorageKey(LocalStorageKey key); | 
|  | static void *allocateLocalStorage(LocalStorageKey key, size_t size); | 
|  | static void *getLocalStorage(LocalStorageKey key); | 
|  | static void freeLocalStorage(LocalStorageKey key); | 
|  |  | 
|  | private: | 
|  | struct Entry | 
|  | { | 
|  | void (*const threadFunction)(void *parameters); | 
|  | void *threadParameters; | 
|  | Event *init; | 
|  | }; | 
|  |  | 
|  | #if defined(_WIN32) | 
|  | static unsigned long __stdcall startFunction(void *parameters); | 
|  | HANDLE handle; | 
|  | #else | 
|  | static void *startFunction(void *parameters); | 
|  | pthread_t handle; | 
|  | #endif | 
|  |  | 
|  | bool hasJoined = false; | 
|  | }; | 
|  |  | 
|  | class Event | 
|  | { | 
|  | friend class Thread; | 
|  |  | 
|  | public: | 
|  | Event(); | 
|  |  | 
|  | ~Event(); | 
|  |  | 
|  | void signal(); | 
|  | void wait(); | 
|  |  | 
|  | private: | 
|  | #if defined(_WIN32) | 
|  | HANDLE handle; | 
|  | #else | 
|  | pthread_cond_t handle; | 
|  | pthread_mutex_t mutex; | 
|  | volatile bool signaled; | 
|  | #endif | 
|  | }; | 
|  |  | 
|  | #if PERF_PROFILE | 
|  | int64_t atomicExchange(int64_t volatile *target, int64_t value); | 
|  | int atomicExchange(int volatile *target, int value); | 
|  | #endif | 
|  |  | 
|  | int atomicIncrement(int volatile *value); | 
|  | int atomicDecrement(int volatile *value); | 
|  | int atomicAdd(int volatile *target, int value); | 
|  | void nop(); | 
|  | } | 
|  |  | 
|  | namespace rr | 
|  | { | 
|  | inline void Thread::yield() | 
|  | { | 
|  | #if defined(_WIN32) | 
|  | Sleep(0); | 
|  | #elif defined(__APPLE__) | 
|  | pthread_yield_np(); | 
|  | #else | 
|  | sched_yield(); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | inline void Thread::sleep(int milliseconds) | 
|  | { | 
|  | #if defined(_WIN32) | 
|  | Sleep(milliseconds); | 
|  | #else | 
|  | usleep(1000 * milliseconds); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | inline Thread::LocalStorageKey Thread::allocateLocalStorageKey(void (*destructor)(void *storage)) | 
|  | { | 
|  | #if defined(_WIN32) | 
|  | return TlsAlloc(); | 
|  | #else | 
|  | LocalStorageKey key; | 
|  | pthread_key_create(&key, destructor); | 
|  | return key; | 
|  | #endif | 
|  | } | 
|  |  | 
|  | inline void Thread::freeLocalStorageKey(LocalStorageKey key) | 
|  | { | 
|  | #if defined(_WIN32) | 
|  | TlsFree(key); | 
|  | #else | 
|  | pthread_key_delete(key);   // Using an invalid key is an error but not undefined behavior. | 
|  | #endif | 
|  | } | 
|  |  | 
|  | inline void *Thread::allocateLocalStorage(LocalStorageKey key, size_t size) | 
|  | { | 
|  | if(key == TLS_OUT_OF_INDEXES) | 
|  | { | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | freeLocalStorage(key); | 
|  |  | 
|  | void *storage = malloc(size); | 
|  |  | 
|  | #if defined(_WIN32) | 
|  | TlsSetValue(key, storage); | 
|  | #else | 
|  | pthread_setspecific(key, storage); | 
|  | #endif | 
|  |  | 
|  | return storage; | 
|  | } | 
|  |  | 
|  | inline void *Thread::getLocalStorage(LocalStorageKey key) | 
|  | { | 
|  | #if defined(_WIN32) | 
|  | return TlsGetValue(key); | 
|  | #else | 
|  | if(key == TLS_OUT_OF_INDEXES)   // Avoid undefined behavior. | 
|  | { | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | return pthread_getspecific(key); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | inline void Thread::freeLocalStorage(LocalStorageKey key) | 
|  | { | 
|  | free(getLocalStorage(key)); | 
|  |  | 
|  | #if defined(_WIN32) | 
|  | TlsSetValue(key, nullptr); | 
|  | #else | 
|  | pthread_setspecific(key, nullptr); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | inline void Event::signal() | 
|  | { | 
|  | #if defined(_WIN32) | 
|  | SetEvent(handle); | 
|  | #else | 
|  | pthread_mutex_lock(&mutex); | 
|  | signaled = true; | 
|  | pthread_cond_signal(&handle); | 
|  | pthread_mutex_unlock(&mutex); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | inline void Event::wait() | 
|  | { | 
|  | #if defined(_WIN32) | 
|  | WaitForSingleObject(handle, INFINITE); | 
|  | #else | 
|  | pthread_mutex_lock(&mutex); | 
|  | while(!signaled) pthread_cond_wait(&handle, &mutex); | 
|  | signaled = false; | 
|  | pthread_mutex_unlock(&mutex); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | #if PERF_PROFILE | 
|  | inline int64_t atomicExchange(volatile int64_t *target, int64_t value) | 
|  | { | 
|  | #if defined(_WIN32) | 
|  | return InterlockedExchange64(target, value); | 
|  | #else | 
|  | int ret; | 
|  | __asm__ __volatile__("lock; xchg8 %x0,(%x1)" : "=r" (ret) :"r" (target), "0" (value) : "memory" ); | 
|  | return ret; | 
|  | #endif | 
|  | } | 
|  |  | 
|  | inline int atomicExchange(volatile int *target, int value) | 
|  | { | 
|  | #if defined(_WIN32) | 
|  | return InterlockedExchange((volatile long*)target, (long)value); | 
|  | #else | 
|  | int ret; | 
|  | __asm__ __volatile__("lock; xchgl %x0,(%x1)" : "=r" (ret) :"r" (target), "0" (value) : "memory" ); | 
|  | return ret; | 
|  | #endif | 
|  | } | 
|  | #endif | 
|  |  | 
|  | inline int atomicIncrement(volatile int *value) | 
|  | { | 
|  | #if defined(_WIN32) | 
|  | return InterlockedIncrement((volatile long*)value); | 
|  | #else | 
|  | return __sync_add_and_fetch(value, 1); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | inline int atomicDecrement(volatile int *value) | 
|  | { | 
|  | #if defined(_WIN32) | 
|  | return InterlockedDecrement((volatile long*)value); | 
|  | #else | 
|  | return __sync_sub_and_fetch(value, 1); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | inline int atomicAdd(volatile int* target, int value) | 
|  | { | 
|  | #if defined(_WIN32) | 
|  | return InterlockedExchangeAdd((volatile long*)target, value) + value; | 
|  | #else | 
|  | return __sync_add_and_fetch(target, value); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | inline void nop() | 
|  | { | 
|  | #if defined(_WIN32) | 
|  | __nop(); | 
|  | #else | 
|  | __asm__ __volatile__ ("nop"); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | #if USE_STD_ATOMIC | 
|  | class AtomicInt | 
|  | { | 
|  | public: | 
|  | AtomicInt() : ai() {} | 
|  | AtomicInt(int i) : ai(i) {} | 
|  |  | 
|  | inline operator int() const { return ai.load(std::memory_order_acquire); } | 
|  | inline void operator=(const AtomicInt& i) { ai.store(i.ai.load(std::memory_order_acquire), std::memory_order_release); } | 
|  | inline void operator=(int i) { ai.store(i, std::memory_order_release); } | 
|  | inline void operator--() { ai.fetch_sub(1, std::memory_order_acq_rel); } | 
|  | inline void operator++() { ai.fetch_add(1, std::memory_order_acq_rel); } | 
|  | inline int operator--(int) { return ai.fetch_sub(1, std::memory_order_acq_rel) - 1; } | 
|  | inline int operator++(int) { return ai.fetch_add(1, std::memory_order_acq_rel) + 1; } | 
|  | inline void operator-=(int i) { ai.fetch_sub(i, std::memory_order_acq_rel); } | 
|  | inline void operator+=(int i) { ai.fetch_add(i, std::memory_order_acq_rel); } | 
|  | private: | 
|  | std::atomic<int> ai; | 
|  | }; | 
|  | #else | 
|  | class AtomicInt | 
|  | { | 
|  | public: | 
|  | AtomicInt() {} | 
|  | AtomicInt(int i) : vi(i) {} | 
|  |  | 
|  | inline operator int() const { return vi; } // Note: this isn't a guaranteed atomic operation | 
|  | inline void operator=(const AtomicInt& i) { atomicExchange(&vi, i.vi); } | 
|  | inline void operator=(int i) { atomicExchange(&vi, i); } | 
|  | inline void operator--() { atomicDecrement(&vi); } | 
|  | inline void operator++() { atomicIncrement(&vi); } | 
|  | inline int operator--(int) { return atomicDecrement(&vi); } | 
|  | inline int operator++(int) { return atomicIncrement(&vi); } | 
|  | inline void operator-=(int i) { atomicAdd(&vi, -i); } | 
|  | inline void operator+=(int i) { atomicAdd(&vi, i); } | 
|  | private: | 
|  | volatile int vi; | 
|  | }; | 
|  | #endif | 
|  | } | 
|  |  | 
|  | #endif   // rr_Thread_hpp |