1 /*
2 * Copyright (c) 2017-2021, The OSKAR Developers.
3 * See the LICENSE file at the top-level directory of this distribution.
4 */
5
6 #include "utility/oskar_thread.h"
7 #include <stdlib.h>
8
9 #ifdef OSKAR_OS_WIN
10 #define WIN32_LEAN_AND_MEAN
11 #include <windows.h>
12 #include <process.h>
13 #else
14 #include <pthread.h>
15 #endif
16
17
18 #ifdef __cplusplus
19 extern "C" {
20 #endif
21
22 /* =========================================================================
23 * MUTEX
24 * =========================================================================*/
25
26 struct oskar_Mutex
27 {
28 #ifdef OSKAR_OS_WIN
29 CRITICAL_SECTION lock;
30 #else
31 pthread_mutex_t lock;
32 #endif
33 };
34
oskar_mutex_init(oskar_Mutex * mutex)35 static void oskar_mutex_init(oskar_Mutex* mutex)
36 {
37 #ifdef OSKAR_OS_WIN
38 InitializeCriticalSectionAndSpinCount(&mutex->lock, 0x0800);
39 #else
40 pthread_mutex_init(&mutex->lock, NULL);
41 #endif
42 }
43
oskar_mutex_uninit(oskar_Mutex * mutex)44 static void oskar_mutex_uninit(oskar_Mutex* mutex)
45 {
46 #ifdef OSKAR_OS_WIN
47 DeleteCriticalSection(&mutex->lock);
48 #else
49 pthread_mutex_destroy(&mutex->lock);
50 #endif
51 }
52
oskar_mutex_create(void)53 oskar_Mutex* oskar_mutex_create(void)
54 {
55 oskar_Mutex* mutex = 0;
56 mutex = (oskar_Mutex*) calloc(1, sizeof(oskar_Mutex));
57 oskar_mutex_init(mutex);
58 return mutex;
59 }
60
oskar_mutex_free(oskar_Mutex * mutex)61 void oskar_mutex_free(oskar_Mutex* mutex)
62 {
63 if (!mutex) return;
64 oskar_mutex_uninit(mutex);
65 free(mutex);
66 }
67
oskar_mutex_lock(oskar_Mutex * mutex)68 void oskar_mutex_lock(oskar_Mutex* mutex)
69 {
70 #ifdef OSKAR_OS_WIN
71 EnterCriticalSection(&mutex->lock);
72 #else
73 pthread_mutex_lock(&mutex->lock);
74 #endif
75 }
76
oskar_mutex_unlock(oskar_Mutex * mutex)77 void oskar_mutex_unlock(oskar_Mutex* mutex)
78 {
79 #ifdef OSKAR_OS_WIN
80 LeaveCriticalSection(&mutex->lock);
81 #else
82 pthread_mutex_unlock(&mutex->lock);
83 #endif
84 }
85
86
87 /* =========================================================================
88 * CONDITION VARIABLE
89 * =========================================================================*/
90
91 struct oskar_ConditionVar
92 {
93 oskar_Mutex lock;
94 #if defined(OSKAR_OS_WIN)
95 CONDITION_VARIABLE var;
96 #else
97 pthread_cond_t var;
98 #endif
99 };
100 typedef struct oskar_ConditionVar oskar_ConditionVar;
101
oskar_condition_init(oskar_ConditionVar * var)102 static void oskar_condition_init(oskar_ConditionVar* var)
103 {
104 oskar_mutex_init(&var->lock);
105 #if defined(OSKAR_OS_WIN)
106 InitializeConditionVariable(&var->var);
107 #else
108 pthread_cond_init(&var->var, NULL);
109 #endif
110 }
111
oskar_condition_uninit(oskar_ConditionVar * var)112 static void oskar_condition_uninit(oskar_ConditionVar* var)
113 {
114 oskar_mutex_uninit(&var->lock);
115 #if defined(OSKAR_OS_WIN)
116 /* No equivalent to pthread_cond_destroy(). */
117 #else
118 pthread_cond_destroy(&var->var);
119 #endif
120 }
121
oskar_condition_lock(oskar_ConditionVar * var)122 static void oskar_condition_lock(oskar_ConditionVar* var)
123 {
124 oskar_mutex_lock(&var->lock);
125 }
126
oskar_condition_unlock(oskar_ConditionVar * var)127 static void oskar_condition_unlock(oskar_ConditionVar* var)
128 {
129 oskar_mutex_unlock(&var->lock);
130 }
131
oskar_condition_notify_all(oskar_ConditionVar * var)132 static void oskar_condition_notify_all(oskar_ConditionVar* var)
133 {
134 #if defined(OSKAR_OS_WIN)
135 WakeAllConditionVariable(&var->var);
136 #else
137 pthread_cond_broadcast(&var->var);
138 #endif
139 }
140
oskar_condition_wait(oskar_ConditionVar * var)141 static void oskar_condition_wait(oskar_ConditionVar* var)
142 {
143 #if defined(OSKAR_OS_WIN)
144 SleepConditionVariableCS(&var->var, &(var->lock.lock), INFINITE);
145 #else
146 pthread_cond_wait(&var->var, &(var->lock.lock));
147 #endif
148 }
149
150
151 /* =========================================================================
152 * THREAD
153 * =========================================================================*/
154
155 struct oskar_Thread
156 {
157 void *(*start_routine)(void*);
158 void *arg;
159 #ifdef OSKAR_OS_WIN
160 HANDLE thread;
161 unsigned int thread_id;
162 #else
163 pthread_t thread;
164 #endif
165 };
166
167 #ifdef OSKAR_OS_WIN
thread_func_win(void * arg)168 unsigned __stdcall thread_func_win(void* arg)
169 {
170 oskar_Thread* thread = (oskar_Thread*)arg;
171 thread->start_routine(thread->arg);
172 _endthreadex(0);
173 return 0;
174 }
175 #endif
176
oskar_thread_create(void * (* start_routine)(void *),void * arg,int detached)177 oskar_Thread* oskar_thread_create(void *(*start_routine)(void*), void* arg,
178 int detached)
179 {
180 #ifndef OSKAR_OS_WIN
181 pthread_attr_t attr;
182 #endif
183 oskar_Thread* thread = 0;
184 thread = (oskar_Thread*) calloc(1, sizeof(oskar_Thread));
185 thread->start_routine = start_routine;
186 thread->arg = arg;
187
188 /* Create the thread and run it. */
189 #ifdef OSKAR_OS_WIN
190 thread->thread = (HANDLE) _beginthreadex(NULL, 0, thread_func_win, thread,
191 (unsigned int) CREATE_SUSPENDED, &(thread->thread_id));
192 if (thread->thread != 0)
193 ResumeThread(thread->thread);
194 #else
195 pthread_attr_init(&attr);
196 pthread_attr_setdetachstate(&attr,
197 detached ? PTHREAD_CREATE_DETACHED : PTHREAD_CREATE_JOINABLE);
198 pthread_create(&thread->thread, &attr, start_routine, arg);
199 pthread_attr_destroy(&attr);
200 #endif
201 return thread;
202 }
203
oskar_thread_free(oskar_Thread * thread)204 void oskar_thread_free(oskar_Thread* thread)
205 {
206 if (!thread) return;
207 #ifdef OSKAR_OS_WIN
208 CloseHandle(thread->thread);
209 #endif
210 free(thread);
211 }
212
oskar_thread_join(oskar_Thread * thread)213 void oskar_thread_join(oskar_Thread* thread)
214 {
215 if (!thread) return;
216 #ifdef OSKAR_OS_WIN
217 WaitForSingleObject(thread->thread, INFINITE);
218 #else
219 pthread_join(thread->thread, NULL);
220 #endif
221 }
222
223
224 /* =========================================================================
225 * BARRIER
226 * =========================================================================*/
227
228 struct oskar_Barrier
229 {
230 oskar_ConditionVar var;
231 unsigned int num_threads, count, iter;
232 };
233
oskar_barrier_create(int num_threads)234 oskar_Barrier* oskar_barrier_create(int num_threads)
235 {
236 oskar_Barrier* barrier = 0;
237 barrier = (oskar_Barrier*) calloc(1, sizeof(oskar_Barrier));
238 oskar_condition_init(&barrier->var);
239 barrier->num_threads = num_threads;
240 barrier->count = num_threads;
241 barrier->iter = 0;
242 return barrier;
243 }
244
oskar_barrier_free(oskar_Barrier * barrier)245 void oskar_barrier_free(oskar_Barrier* barrier)
246 {
247 if (!barrier) return;
248 oskar_condition_uninit(&barrier->var);
249 free(barrier);
250 }
251
oskar_barrier_set_num_threads(oskar_Barrier * barrier,int num_threads)252 void oskar_barrier_set_num_threads(oskar_Barrier* barrier, int num_threads)
253 {
254 barrier->num_threads = num_threads;
255 barrier->count = num_threads;
256 barrier->iter = 0;
257 }
258
oskar_barrier_wait(oskar_Barrier * barrier)259 int oskar_barrier_wait(oskar_Barrier* barrier)
260 {
261 oskar_condition_lock(&barrier->var);
262 {
263 const unsigned int i = barrier->iter;
264 if (--(barrier->count) == 0)
265 {
266 (barrier->iter)++;
267 barrier->count = barrier->num_threads;
268 oskar_condition_notify_all(&barrier->var);
269 oskar_condition_unlock(&barrier->var);
270 return 1;
271 }
272 /* Release lock and block this thread until notified/woken. */
273 /* Allow for spurious wake-ups. */
274 do {
275 oskar_condition_wait(&barrier->var);
276 } while (i == barrier->iter);
277 }
278 oskar_condition_unlock(&barrier->var);
279 return 0;
280 }
281
282 #ifdef __cplusplus
283 }
284 #endif
285