Home | History | Annotate | Download | only in Common
      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();
     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 	#endif
    118 
    119 	int atomicExchange(int volatile *target, int value);
    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 {
    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()
    149 	{
    150 		#if defined(_WIN32)
    151 			return TlsAlloc();
    152 		#else
    153 			LocalStorageKey key;
    154 			pthread_key_create(&key, free);
    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;
    244 			__asm__ __volatile__("lock; xchg8 %0,(%1)" : "=r" (ret) :"r" (target), "0" (value) : "memory" );
    245 			return ret;
    246 		#endif
    247 	}
    248 	#endif
    249 
    250 	inline int atomicExchange(volatile int *target, int value)
    251 	{
    252 		#if defined(_WIN32)
    253 			return InterlockedExchange((volatile long*)target, (long)value);
    254 		#else
    255 			int ret;
    256 			__asm__ __volatile__("lock; xchgl %0,(%1)" : "=r" (ret) :"r" (target), "0" (value) : "memory" );
    257 			return ret;
    258 		#endif
    259 	}
    260 
    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) { 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
    336 }
    337 
    338 #endif   // sw_Thread_hpp
    339