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 (~0)
29 #endif
30 
31 namespace sw
32 {
33 	class Event;
34 
35 	class Thread
36 	{
37 	public:
38 		Thread(void (*threadFunction)(void *parameters), void *parameters);
39 
40 		~Thread();
41 
42 		void join();
43 
44 		static void yield();
45 		static void sleep(int milliseconds);
46 
47 		#if defined(_WIN32)
48 			typedef DWORD LocalStorageKey;
49 		#else
50 			typedef pthread_key_t LocalStorageKey;
51 		#endif
52 
53 		static LocalStorageKey allocateLocalStorageKey();
54 		static void freeLocalStorageKey(LocalStorageKey key);
55 		static void setLocalStorage(LocalStorageKey key, void *value);
56 		static void *getLocalStorage(LocalStorageKey key);
57 
58 	private:
59 		struct Entry
60 		{
61 			void (*const threadFunction)(void *parameters);
62 			void *threadParameters;
63 			Event *init;
64 		};
65 
66 		#if defined(_WIN32)
67 			static unsigned long __stdcall startFunction(void *parameters);
68 			HANDLE handle;
69 		#else
70 			static void *startFunction(void *parameters);
71 			pthread_t handle;
72 		#endif
73 
74 		bool hasJoined = false;
75 	};
76 
77 	class Event
78 	{
79 		friend class Thread;
80 
81 	public:
82 		Event();
83 
84 		~Event();
85 
86 		void signal();
87 		void wait();
88 
89 	private:
90 		#if defined(_WIN32)
91 			HANDLE handle;
92 		#else
93 			pthread_cond_t handle;
94 			pthread_mutex_t mutex;
95 			volatile bool signaled;
96 		#endif
97 	};
98 
99 	#if PERF_PROFILE
100 	int64_t atomicExchange(int64_t volatile *target, int64_t value);
101 	#endif
102 
103 	int atomicExchange(int volatile *target, int value);
104 	int atomicIncrement(int volatile *value);
105 	int atomicDecrement(int volatile *value);
106 	int atomicAdd(int volatile *target, int value);
107 	void nop();
108 }
109 
110 namespace sw
111 {
yield()112 	inline void Thread::yield()
113 	{
114 		#if defined(_WIN32)
115 			Sleep(0);
116 		#elif defined(__APPLE__)
117 			pthread_yield_np();
118 		#else
119 			sched_yield();
120 		#endif
121 	}
122 
sleep(int milliseconds)123 	inline void Thread::sleep(int milliseconds)
124 	{
125 		#if defined(_WIN32)
126 			Sleep(milliseconds);
127 		#else
128 			usleep(1000 * milliseconds);
129 		#endif
130 	}
131 
allocateLocalStorageKey()132 	inline Thread::LocalStorageKey Thread::allocateLocalStorageKey()
133 	{
134 		#if defined(_WIN32)
135 			return TlsAlloc();
136 		#else
137 			LocalStorageKey key;
138 			pthread_key_create(&key, 0);
139 			return key;
140 		#endif
141 	}
142 
freeLocalStorageKey(LocalStorageKey key)143 	inline void Thread::freeLocalStorageKey(LocalStorageKey key)
144 	{
145 		#if defined(_WIN32)
146 			TlsFree(key);
147 		#else
148 			pthread_key_delete(key);
149 		#endif
150 	}
151 
setLocalStorage(LocalStorageKey key,void * value)152 	inline void Thread::setLocalStorage(LocalStorageKey key, void *value)
153 	{
154 		#if defined(_WIN32)
155 			TlsSetValue(key, value);
156 		#else
157 			pthread_setspecific(key, value);
158 		#endif
159 	}
160 
getLocalStorage(LocalStorageKey key)161 	inline void *Thread::getLocalStorage(LocalStorageKey key)
162 	{
163 		#if defined(_WIN32)
164 			return TlsGetValue(key);
165 		#else
166 			return pthread_getspecific(key);
167 		#endif
168 	}
169 
signal()170 	inline void Event::signal()
171 	{
172 		#if defined(_WIN32)
173 			SetEvent(handle);
174 		#else
175 			pthread_mutex_lock(&mutex);
176 			signaled = true;
177 			pthread_cond_signal(&handle);
178 			pthread_mutex_unlock(&mutex);
179 		#endif
180 	}
181 
wait()182 	inline void Event::wait()
183 	{
184 		#if defined(_WIN32)
185 			WaitForSingleObject(handle, INFINITE);
186 		#else
187 			pthread_mutex_lock(&mutex);
188 			while(!signaled) pthread_cond_wait(&handle, &mutex);
189 			signaled = false;
190 			pthread_mutex_unlock(&mutex);
191 		#endif
192 	}
193 
194 	#if PERF_PROFILE
atomicExchange(volatile int64_t * target,int64_t value)195 	inline int64_t atomicExchange(volatile int64_t *target, int64_t value)
196 	{
197 		#if defined(_WIN32)
198 			return InterlockedExchange64(target, value);
199 		#else
200 			int ret;
201 			__asm__ __volatile__("lock; xchg8 %0,(%1)" : "=r" (ret) :"r" (target), "0" (value) : "memory" );
202 			return ret;
203 		#endif
204 	}
205 	#endif
206 
atomicExchange(volatile int * target,int value)207 	inline int atomicExchange(volatile int *target, int value)
208 	{
209 		#if defined(_WIN32)
210 			return InterlockedExchange((volatile long*)target, (long)value);
211 		#else
212 			int ret;
213 			__asm__ __volatile__("lock; xchgl %0,(%1)" : "=r" (ret) :"r" (target), "0" (value) : "memory" );
214 			return ret;
215 		#endif
216 	}
217 
atomicIncrement(volatile int * value)218 	inline int atomicIncrement(volatile int *value)
219 	{
220 		#if defined(_WIN32)
221 			return InterlockedIncrement((volatile long*)value);
222 		#else
223 			return __sync_add_and_fetch(value, 1);
224 		#endif
225 	}
226 
atomicDecrement(volatile int * value)227 	inline int atomicDecrement(volatile int *value)
228 	{
229 		#if defined(_WIN32)
230 			return InterlockedDecrement((volatile long*)value);
231 		#else
232 			return __sync_sub_and_fetch(value, 1);
233 		#endif
234 	}
235 
atomicAdd(volatile int * target,int value)236 	inline int atomicAdd(volatile int* target, int value)
237 	{
238 		#if defined(_MSC_VER)
239 			return InterlockedExchangeAdd((volatile long*)target, value) + value;
240 		#else
241 			return __sync_add_and_fetch(target, value);
242 		#endif
243 	}
244 
nop()245 	inline void nop()
246 	{
247 		#if defined(_WIN32)
248 			__nop();
249 		#else
250 			__asm__ __volatile__ ("nop");
251 		#endif
252 	}
253 }
254 
255 #endif   // sw_Thread_hpp
256