1 // SONIC ROBO BLAST 2
2 //-----------------------------------------------------------------------------
3 // Copyright (C) 2020 by James R.
4 //
5 // This program is free software distributed under the
6 // terms of the GNU General Public License, version 2.
7 // See the 'LICENSE' file for more details.
8 //-----------------------------------------------------------------------------
9 /// \file i_threads.c
10 /// \brief Multithreading abstraction
11
12 #include "../doomdef.h"
13 #include "../i_threads.h"
14
15 #include <SDL.h>
16
17 typedef void * (*Create_fn)(void);
18
19 struct Link;
20 struct Thread;
21
22 typedef struct Link * Link;
23 typedef struct Thread * Thread;
24
25 struct Link
26 {
27 void * data;
28 Link next;
29 Link prev;
30 };
31
32 struct Thread
33 {
34 I_thread_fn entry;
35 void * userdata;
36
37 SDL_Thread * thread;
38 };
39
40 static Link i_thread_pool;
41 static Link i_mutex_pool;
42 static Link i_cond_pool;
43
44 static I_mutex i_thread_pool_mutex;
45 static I_mutex i_mutex_pool_mutex;
46 static I_mutex i_cond_pool_mutex;
47
48 static SDL_atomic_t i_threads_running = {1};
49
50 static Link
Insert_link(Link * head,Link link)51 Insert_link (
52 Link * head,
53 Link link
54 ){
55 link->prev = NULL;
56 link->next = (*head);
57 if ((*head))
58 (*head)->prev = link;
59 (*head) = link;
60 return link;
61 }
62
63 static void
Free_link(Link * head,Link link)64 Free_link (
65 Link * head,
66 Link link
67 ){
68 if (link->prev)
69 link->prev->next = link->next;
70 else
71 (*head) = link->next;
72
73 if (link->next)
74 link->next->prev = link->prev;
75
76 free(link->data);
77 free(link);
78 }
79
80 static Link
New_link(void * data)81 New_link (void *data)
82 {
83 Link link;
84
85 link = malloc(sizeof *link);
86
87 if (! link)
88 abort();
89
90 link->data = data;
91
92 return link;
93 }
94
95 static void *
Identity(Link * pool_anchor,I_mutex pool_mutex,void ** anchor,Create_fn create_fn)96 Identity (
97 Link * pool_anchor,
98 I_mutex pool_mutex,
99
100 void ** anchor,
101
102 Create_fn create_fn
103 ){
104 void * id;
105
106 id = SDL_AtomicGetPtr(anchor);
107
108 if (! id)
109 {
110 I_lock_mutex(&pool_mutex);
111 {
112 id = SDL_AtomicGetPtr(anchor);
113
114 if (! id)
115 {
116 id = (*create_fn)();
117
118 if (! id)
119 abort();
120
121 Insert_link(pool_anchor, New_link(id));
122
123 SDL_AtomicSetPtr(anchor, id);
124 }
125 }
126 I_unlock_mutex(pool_mutex);
127 }
128
129 return id;
130 }
131
132 static int
Worker(Link link)133 Worker (
134 Link link
135 ){
136 Thread th;
137
138 th = link->data;
139
140 (*th->entry)(th->userdata);
141
142 if (SDL_AtomicGet(&i_threads_running))
143 {
144 I_lock_mutex(&i_thread_pool_mutex);
145 {
146 if (SDL_AtomicGet(&i_threads_running))
147 {
148 SDL_DetachThread(th->thread);
149 Free_link(&i_thread_pool, link);
150 }
151 }
152 I_unlock_mutex(i_thread_pool_mutex);
153 }
154
155 return 0;
156 }
157
158 void
I_spawn_thread(const char * name,I_thread_fn entry,void * userdata)159 I_spawn_thread (
160 const char * name,
161 I_thread_fn entry,
162 void * userdata
163 ){
164 Link link;
165 Thread th;
166
167 th = malloc(sizeof *th);
168
169 if (! th)
170 abort();/* this is pretty GNU of me */
171
172 th->entry = entry;
173 th->userdata = userdata;
174
175 I_lock_mutex(&i_thread_pool_mutex);
176 {
177 link = Insert_link(&i_thread_pool, New_link(th));
178
179 if (SDL_AtomicGet(&i_threads_running))
180 {
181 th->thread = SDL_CreateThread(
182 (SDL_ThreadFunction)Worker,
183 name,
184 link
185 );
186
187 if (! th->thread)
188 abort();
189 }
190 }
191 I_unlock_mutex(i_thread_pool_mutex);
192 }
193
194 int
I_thread_is_stopped(void)195 I_thread_is_stopped (void)
196 {
197 return ( ! SDL_AtomicGet(&i_threads_running) );
198 }
199
200 void
I_start_threads(void)201 I_start_threads (void)
202 {
203 i_thread_pool_mutex = SDL_CreateMutex();
204 i_mutex_pool_mutex = SDL_CreateMutex();
205 i_cond_pool_mutex = SDL_CreateMutex();
206
207 if (!(
208 i_thread_pool_mutex &&
209 i_mutex_pool_mutex &&
210 i_cond_pool_mutex
211 )){
212 abort();
213 }
214 }
215
216 void
I_stop_threads(void)217 I_stop_threads (void)
218 {
219 Link link;
220 Link next;
221
222 Thread th;
223 SDL_mutex * mutex;
224 SDL_cond * cond;
225
226 if (i_threads_running.value)
227 {
228 /* rely on the good will of thread-san */
229 SDL_AtomicSet(&i_threads_running, 0);
230
231 I_lock_mutex(&i_thread_pool_mutex);
232 {
233 for (
234 link = i_thread_pool;
235 link;
236 link = next
237 ){
238 next = link->next;
239 th = link->data;
240
241 SDL_WaitThread(th->thread, NULL);
242
243 free(th);
244 free(link);
245 }
246 }
247 I_unlock_mutex(i_thread_pool_mutex);
248
249 for (
250 link = i_mutex_pool;
251 link;
252 link = next
253 ){
254 next = link->next;
255 mutex = link->data;
256
257 SDL_DestroyMutex(mutex);
258
259 free(link);
260 }
261
262 for (
263 link = i_cond_pool;
264 link;
265 link = next
266 ){
267 next = link->next;
268 cond = link->data;
269
270 SDL_DestroyCond(cond);
271
272 free(link);
273 }
274
275 SDL_DestroyMutex(i_thread_pool_mutex);
276 SDL_DestroyMutex(i_mutex_pool_mutex);
277 SDL_DestroyMutex(i_cond_pool_mutex);
278 }
279 }
280
281 void
I_lock_mutex(I_mutex * anchor)282 I_lock_mutex (
283 I_mutex * anchor
284 ){
285 SDL_mutex * mutex;
286
287 mutex = Identity(
288 &i_mutex_pool,
289 i_mutex_pool_mutex,
290 anchor,
291 (Create_fn)SDL_CreateMutex
292 );
293
294 if (SDL_LockMutex(mutex) == -1)
295 abort();
296 }
297
298 void
I_unlock_mutex(I_mutex id)299 I_unlock_mutex (
300 I_mutex id
301 ){
302 if (SDL_UnlockMutex(id) == -1)
303 abort();
304 }
305
306 void
I_hold_cond(I_cond * cond_anchor,I_mutex mutex_id)307 I_hold_cond (
308 I_cond * cond_anchor,
309 I_mutex mutex_id
310 ){
311 SDL_cond * cond;
312
313 cond = Identity(
314 &i_cond_pool,
315 i_cond_pool_mutex,
316 cond_anchor,
317 (Create_fn)SDL_CreateCond
318 );
319
320 if (SDL_CondWait(cond, mutex_id) == -1)
321 abort();
322 }
323
324 void
I_wake_one_cond(I_cond * anchor)325 I_wake_one_cond (
326 I_cond * anchor
327 ){
328 SDL_cond * cond;
329
330 cond = Identity(
331 &i_cond_pool,
332 i_cond_pool_mutex,
333 anchor,
334 (Create_fn)SDL_CreateCond
335 );
336
337 if (SDL_CondSignal(cond) == -1)
338 abort();
339 }
340
341 void
I_wake_all_cond(I_cond * anchor)342 I_wake_all_cond (
343 I_cond * anchor
344 ){
345 SDL_cond * cond;
346
347 cond = Identity(
348 &i_cond_pool,
349 i_cond_pool_mutex,
350 anchor,
351 (Create_fn)SDL_CreateCond
352 );
353
354 if (SDL_CondBroadcast(cond) == -1)
355 abort();
356 }
357