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