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