1 /* ______ ___ ___
2 * /\ _ \ /\_ \ /\_ \
3 * \ \ \L\ \\//\ \ \//\ \ __ __ _ __ ___
4 * \ \ __ \ \ \ \ \ \ \ /'__`\ /'_ `\/\`'__\/ __`\
5 * \ \ \/\ \ \_\ \_ \_\ \_/\ __//\ \L\ \ \ \//\ \L\ \
6 * \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
7 * \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/
8 * /\____/
9 * \_/__/
10 *
11 * Internal cross-platform threading API for Windows.
12 *
13 * By Peter Wang.
14 *
15 * See readme.txt for copyright information.
16 */
17
18
19 #include "allegro5/allegro.h"
20 #include "allegro5/internal/aintern.h"
21 #include "allegro5/internal/aintern_thread.h"
22
23 #include <mmsystem.h>
24 #include <process.h>
25
26
27
28 /* threads */
29
thread_proc_trampoline(void * data)30 static unsigned __stdcall thread_proc_trampoline(void *data)
31 {
32 _AL_THREAD *thread = data;
33 (*thread->proc)(thread, thread->arg);
34
35 /* _endthreadex does not automatically close the thread handle,
36 * unlike _endthread. We rely on this in al_join_thread().
37 */
38 _endthreadex(0);
39 return 0;
40 }
41
42
_al_thread_create(_AL_THREAD * thread,void (* proc)(_AL_THREAD *,void *),void * arg)43 void _al_thread_create(_AL_THREAD *thread, void (*proc)(_AL_THREAD*, void*), void *arg)
44 {
45 ASSERT(thread);
46 ASSERT(proc);
47 {
48 InitializeCriticalSection(&thread->cs);
49
50 thread->should_stop = false;
51 thread->proc = proc;
52 thread->arg = arg;
53
54 thread->thread = (void *)_beginthreadex(NULL, 0,
55 thread_proc_trampoline, thread, 0, NULL);
56 }
57 }
58
59
_al_thread_create_with_stacksize(_AL_THREAD * thread,void (* proc)(_AL_THREAD *,void *),void * arg,size_t stacksize)60 void _al_thread_create_with_stacksize(_AL_THREAD* thread, void (*proc)(_AL_THREAD*, void*), void *arg, size_t stacksize)
61 {
62 ASSERT(thread);
63 ASSERT(proc);
64 {
65 InitializeCriticalSection(&thread->cs);
66
67 thread->should_stop = false;
68 thread->proc = proc;
69 thread->arg = arg;
70
71 thread->thread = (void *)_beginthreadex(NULL, stacksize,
72 thread_proc_trampoline, thread, 0, NULL);
73 }
74 }
75
76
_al_thread_set_should_stop(_AL_THREAD * thread)77 void _al_thread_set_should_stop(_AL_THREAD *thread)
78 {
79 ASSERT(thread);
80
81 EnterCriticalSection(&thread->cs);
82 {
83 thread->should_stop = true;
84 }
85 LeaveCriticalSection(&thread->cs);
86 }
87
88
89
_al_thread_join(_AL_THREAD * thread)90 void _al_thread_join(_AL_THREAD *thread)
91 {
92 ASSERT(thread);
93
94 _al_thread_set_should_stop(thread);
95 WaitForSingleObject(thread->thread, INFINITE);
96
97 CloseHandle(thread->thread);
98 DeleteCriticalSection(&thread->cs);
99 }
100
101
_al_thread_detach(_AL_THREAD * thread)102 void _al_thread_detach(_AL_THREAD *thread)
103 {
104 ASSERT(thread);
105
106 CloseHandle(thread->thread);
107 DeleteCriticalSection(&thread->cs);
108 }
109
110
111
112 /* mutexes */
113
_al_mutex_init(_AL_MUTEX * mutex)114 void _al_mutex_init(_AL_MUTEX *mutex)
115 {
116 ASSERT(mutex);
117
118 if (!mutex->cs)
119 mutex->cs = al_malloc(sizeof *mutex->cs);
120 ASSERT(mutex->cs);
121 if (mutex->cs)
122 InitializeCriticalSection(mutex->cs);
123 else
124 abort();
125 }
126
127
_al_mutex_init_recursive(_AL_MUTEX * mutex)128 void _al_mutex_init_recursive(_AL_MUTEX *mutex)
129 {
130 /* These are the same on Windows. */
131 _al_mutex_init(mutex);
132 }
133
134
_al_mutex_destroy(_AL_MUTEX * mutex)135 void _al_mutex_destroy(_AL_MUTEX *mutex)
136 {
137 ASSERT(mutex);
138
139 if (mutex->cs) {
140 DeleteCriticalSection(mutex->cs);
141 al_free(mutex->cs);
142 mutex->cs = NULL;
143 }
144 }
145
146
147 /* condition variables */
148
149 /*
150 * The algorithm used was originally designed for the pthread-win32
151 * package. I translated the pseudo-code below line for line.
152 *
153 * --- From pthread-win32: ---
154 * Algorithm:
155 * The algorithm used in this implementation is that developed by
156 * Alexander Terekhov in colaboration with Louis Thomas. The bulk
157 * of the discussion is recorded in the file README.CV, which contains
158 * several generations of both colaborators original algorithms. The final
159 * algorithm used here is the one referred to as
160 *
161 * Algorithm 8a / IMPL_SEM,UNBLOCK_STRATEGY == UNBLOCK_ALL
162 *
163 * presented below in pseudo-code as it appeared:
164 *
165 * [snip]
166 * -------------------------------------------------------------
167 *
168 * Algorithm 9 / IMPL_SEM,UNBLOCK_STRATEGY == UNBLOCK_ALL
169 *
170 * presented below in pseudo-code; basically 8a...
171 * ...BUT W/O "spurious wakes" prevention:
172 *
173 *
174 * given:
175 * semBlockLock - bin.semaphore
176 * semBlockQueue - semaphore
177 * mtxExternal - mutex or CS
178 * mtxUnblockLock - mutex or CS
179 * nWaitersGone - int
180 * nWaitersBlocked - int
181 * nWaitersToUnblock - int
182 *
183 * wait( timeout ) {
184 *
185 * [auto: register int result ] // error checking omitted
186 * [auto: register int nSignalsWasLeft ]
187 *
188 * sem_wait( semBlockLock );
189 * ++nWaitersBlocked;
190 * sem_post( semBlockLock );
191 *
192 * unlock( mtxExternal );
193 * bTimedOut = sem_wait( semBlockQueue,timeout );
194 *
195 * lock( mtxUnblockLock );
196 * if ( 0 != (nSignalsWasLeft = nWaitersToUnblock) ) {
197 * --nWaitersToUnblock;
198 * }
199 * else if ( INT_MAX/2 == ++nWaitersGone ) { // timeout/canceled or
200 * // spurious semaphore :-)
201 * sem_wait( semBlockLock );
202 * nWaitersBlocked -= nWaitersGone; // something is going on here
203 * // - test of timeouts? :-)
204 * sem_post( semBlockLock );
205 * nWaitersGone = 0;
206 * }
207 * unlock( mtxUnblockLock );
208 *
209 * if ( 1 == nSignalsWasLeft ) {
210 * sem_post( semBlockLock ); // open the gate
211 * }
212 *
213 * lock( mtxExternal );
214 *
215 * return ( bTimedOut ) ? ETIMEOUT : 0;
216 * }
217 *
218 * signal(bAll) {
219 *
220 * [auto: register int result ]
221 * [auto: register int nSignalsToIssue]
222 *
223 * lock( mtxUnblockLock );
224 *
225 * if ( 0 != nWaitersToUnblock ) { // the gate is closed!!!
226 * if ( 0 == nWaitersBlocked ) { // NO-OP
227 * return unlock( mtxUnblockLock );
228 * }
229 * if (bAll) {
230 * nWaitersToUnblock += nSignalsToIssue=nWaitersBlocked;
231 * nWaitersBlocked = 0;
232 * }
233 * else {
234 * nSignalsToIssue = 1;
235 * ++nWaitersToUnblock;
236 * --nWaitersBlocked;
237 * }
238 * }
239 * else if ( nWaitersBlocked > nWaitersGone ) { // HARMLESS RACE CONDITION!
240 * sem_wait( semBlockLock ); // close the gate
241 * if ( 0 != nWaitersGone ) {
242 * nWaitersBlocked -= nWaitersGone;
243 * nWaitersGone = 0;
244 * }
245 * if (bAll) {
246 * nSignalsToIssue = nWaitersToUnblock = nWaitersBlocked;
247 * nWaitersBlocked = 0;
248 * }
249 * else {
250 * nSignalsToIssue = nWaitersToUnblock = 1;
251 * --nWaitersBlocked;
252 * }
253 * }
254 * else { // NO-OP
255 * return unlock( mtxUnblockLock );
256 * }
257 *
258 * unlock( mtxUnblockLock );
259 * sem_post( semBlockQueue,nSignalsToIssue );
260 * return result;
261 * }
262 * -------------------------------------------------------------
263 */
264
265
_al_cond_init(_AL_COND * cond)266 void _al_cond_init(_AL_COND *cond)
267 {
268 cond->nWaitersBlocked = 0;
269 cond->nWaitersGone = 0;
270 cond->nWaitersToUnblock = 0;
271 cond->semBlockQueue = CreateSemaphore(NULL, 0, INT_MAX, NULL);
272 InitializeCriticalSection(&cond->semBlockLock);
273 InitializeCriticalSection(&cond->mtxUnblockLock);
274 }
275
276
_al_cond_destroy(_AL_COND * cond)277 void _al_cond_destroy(_AL_COND *cond)
278 {
279 DeleteCriticalSection(&cond->mtxUnblockLock);
280 DeleteCriticalSection(&cond->semBlockLock);
281 CloseHandle(cond->semBlockQueue);
282 }
283
284
285 /* returns -1 on timeout */
cond_wait(_AL_COND * cond,_AL_MUTEX * mtxExternal,DWORD timeout)286 static int cond_wait(_AL_COND *cond, _AL_MUTEX *mtxExternal, DWORD timeout)
287 {
288 int nSignalsWasLeft;
289 bool bTimedOut;
290 DWORD dwWaitResult;
291
292 EnterCriticalSection(&cond->semBlockLock);
293 ++cond->nWaitersBlocked;
294 LeaveCriticalSection(&cond->semBlockLock);
295
296 _al_mutex_unlock(mtxExternal);
297
298 dwWaitResult = WaitForSingleObject(cond->semBlockQueue, timeout);
299 if (dwWaitResult == WAIT_TIMEOUT)
300 bTimedOut = true;
301 else if (dwWaitResult == WAIT_OBJECT_0)
302 bTimedOut = false;
303 else {
304 /* bad! what to do? */
305 _al_mutex_lock(mtxExternal);
306 ASSERT(false);
307 return 0;
308 }
309
310 EnterCriticalSection(&cond->mtxUnblockLock);
311 if (0 != (nSignalsWasLeft = cond->nWaitersToUnblock)) {
312 --(cond->nWaitersToUnblock);
313 }
314 else if (INT_MAX/2 == ++(cond->nWaitersGone)) { /* timeout/canceled or spurious semaphore :-) */
315 EnterCriticalSection(&cond->semBlockLock);
316 cond->nWaitersBlocked -= cond->nWaitersGone; /* something is going on here
317 - test of timeouts? :-) */
318 LeaveCriticalSection(&cond->semBlockLock);
319 cond->nWaitersGone = 0;
320 }
321 LeaveCriticalSection(&cond->mtxUnblockLock);
322
323 if (1 == nSignalsWasLeft) {
324 LeaveCriticalSection(&cond->semBlockLock); /* open the gate */
325 }
326
327 _al_mutex_lock(mtxExternal);
328
329 return bTimedOut ? -1 : 0;
330 }
331
332
_al_cond_wait(_AL_COND * cond,_AL_MUTEX * mtxExternal)333 void _al_cond_wait(_AL_COND *cond, _AL_MUTEX *mtxExternal)
334 {
335 int result;
336
337 ASSERT(cond);
338 ASSERT(mtxExternal);
339
340 result = cond_wait(cond, mtxExternal, INFINITE);
341 ASSERT(result != -1);
342 (void)result;
343 }
344
345
_al_cond_timedwait(_AL_COND * cond,_AL_MUTEX * mtxExternal,const ALLEGRO_TIMEOUT * timeout)346 int _al_cond_timedwait(_AL_COND *cond, _AL_MUTEX *mtxExternal,
347 const ALLEGRO_TIMEOUT *timeout)
348 {
349 ALLEGRO_TIMEOUT_WIN *win_timeout = (ALLEGRO_TIMEOUT_WIN *) timeout;
350 DWORD now;
351 DWORD rel_msecs;
352
353 ASSERT(cond);
354 ASSERT(mtxExternal);
355
356 now = timeGetTime();
357 rel_msecs = win_timeout->abstime - now;
358 if (rel_msecs > INT_MAX) {
359 rel_msecs = 0;
360 }
361
362 return cond_wait(cond, mtxExternal, rel_msecs);
363 }
364
365
cond_signal(_AL_COND * cond,bool bAll)366 static void cond_signal(_AL_COND *cond, bool bAll)
367 {
368 int nSignalsToIssue;
369
370 EnterCriticalSection(&cond->mtxUnblockLock);
371
372 if (0 != cond->nWaitersToUnblock) { /* the gate is closed!!! */
373 if (0 == cond->nWaitersBlocked) { /* NO-OP */
374 LeaveCriticalSection(&cond->mtxUnblockLock);
375 return;
376 }
377 if (bAll) {
378 cond->nWaitersToUnblock += (nSignalsToIssue = cond->nWaitersBlocked);
379 cond->nWaitersBlocked = 0;
380 }
381 else {
382 nSignalsToIssue = 1;
383 ++cond->nWaitersToUnblock;
384 --cond->nWaitersBlocked;
385 }
386 }
387 else if (cond->nWaitersBlocked > cond->nWaitersGone) { /* HARMLESS RACE CONDITION! */
388 EnterCriticalSection(&cond->semBlockLock); /* close the gate */
389 if (0 != cond->nWaitersGone) {
390 cond->nWaitersBlocked -= cond->nWaitersGone;
391 cond->nWaitersGone = 0;
392 }
393 if (bAll) {
394 nSignalsToIssue = (cond->nWaitersToUnblock = cond->nWaitersBlocked);
395 cond->nWaitersBlocked = 0;
396 }
397 else {
398 nSignalsToIssue = cond->nWaitersToUnblock = 1;
399 --cond->nWaitersBlocked;
400 }
401 }
402 else { /* NO-OP */
403 LeaveCriticalSection(&cond->mtxUnblockLock);
404 return;
405 }
406
407 LeaveCriticalSection(&cond->mtxUnblockLock);
408 ReleaseSemaphore(cond->semBlockQueue, nSignalsToIssue, NULL);
409 return;
410 }
411
412
_al_cond_broadcast(_AL_COND * cond)413 void _al_cond_broadcast(_AL_COND *cond)
414 {
415 cond_signal(cond, true);
416 }
417
418
_al_cond_signal(_AL_COND * cond)419 void _al_cond_signal(_AL_COND *cond)
420 {
421 cond_signal(cond, false);
422 }
423
424
425 /* vim: set sts=3 sw=3 et: */
426