Add a comment about waiting to kill the event thread
[openal-soft.git] / common / threads.c
blob6cfe383baf47f6f4f9e3717e658a2dedcf767735
1 /**
2 * OpenAL cross platform audio library
3 * Copyright (C) 1999-2007 by authors.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 * Or go to http://www.gnu.org/copyleft/lgpl.html
21 #include "config.h"
23 #include "threads.h"
25 #include <stdlib.h>
26 #include <string.h>
27 #include <errno.h>
29 #include "uintmap.h"
32 extern inline althrd_t althrd_current(void);
33 extern inline int althrd_equal(althrd_t thr0, althrd_t thr1);
34 extern inline void althrd_exit(int res);
35 extern inline void althrd_yield(void);
37 extern inline int almtx_lock(almtx_t *mtx);
38 extern inline int almtx_unlock(almtx_t *mtx);
39 extern inline int almtx_trylock(almtx_t *mtx);
41 extern inline void *altss_get(altss_t tss_id);
42 extern inline int altss_set(altss_t tss_id, void *val);
45 #ifndef UNUSED
46 #if defined(__cplusplus)
47 #define UNUSED(x)
48 #elif defined(__GNUC__)
49 #define UNUSED(x) UNUSED_##x __attribute__((unused))
50 #elif defined(__LCLINT__)
51 #define UNUSED(x) /*@unused@*/ x
52 #else
53 #define UNUSED(x) x
54 #endif
55 #endif
58 #define THREAD_STACK_SIZE (2*1024*1024) /* 2MB */
60 #ifdef _WIN32
62 #define WIN32_LEAN_AND_MEAN
63 #include <windows.h>
64 #include <mmsystem.h>
67 /* An associative map of uint:void* pairs. The key is the unique Thread ID and
68 * the value is the thread HANDLE. The thread ID is passed around as the
69 * althrd_t since there is only one ID per thread, whereas a thread may be
70 * referenced by multiple different HANDLEs. This map allows retrieving the
71 * original handle which is needed to join the thread and get its return value.
73 static UIntMap ThrdIdHandle = UINTMAP_STATIC_INITIALIZE;
75 /* An associative map of uint:void* pairs. The key is the TLS index (given by
76 * TlsAlloc), and the value is the altss_dtor_t callback. When a thread exits,
77 * we iterate over the TLS indices for their thread-local value and call the
78 * destructor function with it if they're both not NULL.
80 static UIntMap TlsDestructors = UINTMAP_STATIC_INITIALIZE;
83 void althrd_setname(althrd_t thr, const char *name)
85 #if defined(_MSC_VER)
86 #define MS_VC_EXCEPTION 0x406D1388
87 #pragma pack(push,8)
88 struct {
89 DWORD dwType; // Must be 0x1000.
90 LPCSTR szName; // Pointer to name (in user addr space).
91 DWORD dwThreadID; // Thread ID (-1=caller thread).
92 DWORD dwFlags; // Reserved for future use, must be zero.
93 } info;
94 #pragma pack(pop)
95 info.dwType = 0x1000;
96 info.szName = name;
97 info.dwThreadID = thr;
98 info.dwFlags = 0;
100 __try {
101 RaiseException(MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info);
103 __except(EXCEPTION_CONTINUE_EXECUTION) {
105 #undef MS_VC_EXCEPTION
106 #else
107 (void)thr;
108 (void)name;
109 #endif
113 typedef struct thread_cntr {
114 althrd_start_t func;
115 void *arg;
116 } thread_cntr;
118 static DWORD WINAPI althrd_starter(void *arg)
120 thread_cntr cntr;
121 memcpy(&cntr, arg, sizeof(cntr));
122 free(arg);
124 return (DWORD)((*cntr.func)(cntr.arg));
128 int althrd_create(althrd_t *thr, althrd_start_t func, void *arg)
130 thread_cntr *cntr;
131 DWORD thrid;
132 HANDLE hdl;
134 cntr = malloc(sizeof(*cntr));
135 if(!cntr) return althrd_nomem;
137 cntr->func = func;
138 cntr->arg = arg;
140 hdl = CreateThread(NULL, THREAD_STACK_SIZE, althrd_starter, cntr, 0, &thrid);
141 if(!hdl)
143 free(cntr);
144 return althrd_error;
146 InsertUIntMapEntry(&ThrdIdHandle, thrid, hdl);
148 *thr = thrid;
149 return althrd_success;
152 int althrd_detach(althrd_t thr)
154 HANDLE hdl = RemoveUIntMapKey(&ThrdIdHandle, thr);
155 if(!hdl) return althrd_error;
157 CloseHandle(hdl);
158 return althrd_success;
161 int althrd_join(althrd_t thr, int *res)
163 DWORD code;
165 HANDLE hdl = RemoveUIntMapKey(&ThrdIdHandle, thr);
166 if(!hdl) return althrd_error;
168 WaitForSingleObject(hdl, INFINITE);
169 GetExitCodeThread(hdl, &code);
170 CloseHandle(hdl);
172 if(res != NULL)
173 *res = (int)code;
174 return althrd_success;
177 int althrd_sleep(const struct timespec *ts, struct timespec* UNUSED(rem))
179 DWORD msec;
181 if(ts->tv_sec < 0 || ts->tv_sec >= (0x7fffffff / 1000) ||
182 ts->tv_nsec < 0 || ts->tv_nsec >= 1000000000)
183 return -2;
185 msec = (DWORD)(ts->tv_sec * 1000);
186 msec += (DWORD)((ts->tv_nsec+999999) / 1000000);
187 Sleep(msec);
189 return 0;
193 int almtx_init(almtx_t *mtx, int type)
195 if(!mtx) return althrd_error;
197 type &= ~almtx_recursive;
198 if(type != almtx_plain)
199 return althrd_error;
201 InitializeCriticalSection(mtx);
202 return althrd_success;
205 void almtx_destroy(almtx_t *mtx)
207 DeleteCriticalSection(mtx);
210 #if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0600
211 int alcnd_init(alcnd_t *cond)
213 InitializeConditionVariable(cond);
214 return althrd_success;
217 int alcnd_signal(alcnd_t *cond)
219 WakeConditionVariable(cond);
220 return althrd_success;
223 int alcnd_broadcast(alcnd_t *cond)
225 WakeAllConditionVariable(cond);
226 return althrd_success;
229 int alcnd_wait(alcnd_t *cond, almtx_t *mtx)
231 if(SleepConditionVariableCS(cond, mtx, INFINITE) != 0)
232 return althrd_success;
233 return althrd_error;
236 void alcnd_destroy(alcnd_t* UNUSED(cond))
238 /* Nothing to delete? */
241 #else
243 /* WARNING: This is a rather poor implementation of condition variables, with
244 * known problems. However, it's simple, efficient, and good enough for now to
245 * not require Vista. Based on "Strategies for Implementing POSIX Condition
246 * Variables" by Douglas C. Schmidt and Irfan Pyarali:
247 * http://www.cs.wustl.edu/~schmidt/win32-cv-1.html
249 /* A better solution may be using Wine's implementation. It requires internals
250 * (NtCreateKeyedEvent, NtReleaseKeyedEvent, and NtWaitForKeyedEvent) from
251 * ntdll, and implemention of exchange and compare-exchange for RefCounts.
254 typedef struct {
255 RefCount wait_count;
257 HANDLE events[2];
258 } _int_alcnd_t;
259 enum {
260 SIGNAL = 0,
261 BROADCAST = 1
264 int alcnd_init(alcnd_t *cond)
266 _int_alcnd_t *icond = calloc(1, sizeof(*icond));
267 if(!icond) return althrd_nomem;
269 InitRef(&icond->wait_count, 0);
271 icond->events[SIGNAL] = CreateEventW(NULL, FALSE, FALSE, NULL);
272 icond->events[BROADCAST] = CreateEventW(NULL, TRUE, FALSE, NULL);
273 if(!icond->events[SIGNAL] || !icond->events[BROADCAST])
275 if(icond->events[SIGNAL])
276 CloseHandle(icond->events[SIGNAL]);
277 if(icond->events[BROADCAST])
278 CloseHandle(icond->events[BROADCAST]);
279 free(icond);
280 return althrd_error;
283 cond->Ptr = icond;
284 return althrd_success;
287 int alcnd_signal(alcnd_t *cond)
289 _int_alcnd_t *icond = cond->Ptr;
290 if(ReadRef(&icond->wait_count) > 0)
291 SetEvent(icond->events[SIGNAL]);
292 return althrd_success;
295 int alcnd_broadcast(alcnd_t *cond)
297 _int_alcnd_t *icond = cond->Ptr;
298 if(ReadRef(&icond->wait_count) > 0)
299 SetEvent(icond->events[BROADCAST]);
300 return althrd_success;
303 int alcnd_wait(alcnd_t *cond, almtx_t *mtx)
305 _int_alcnd_t *icond = cond->Ptr;
306 int res;
308 IncrementRef(&icond->wait_count);
309 LeaveCriticalSection(mtx);
311 res = WaitForMultipleObjects(2, icond->events, FALSE, INFINITE);
313 if(DecrementRef(&icond->wait_count) == 0 && res == WAIT_OBJECT_0+BROADCAST)
314 ResetEvent(icond->events[BROADCAST]);
315 EnterCriticalSection(mtx);
317 return althrd_success;
320 void alcnd_destroy(alcnd_t *cond)
322 _int_alcnd_t *icond = cond->Ptr;
323 CloseHandle(icond->events[SIGNAL]);
324 CloseHandle(icond->events[BROADCAST]);
325 free(icond);
327 #endif /* defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0600 */
330 int alsem_init(alsem_t *sem, unsigned int initial)
332 *sem = CreateSemaphore(NULL, initial, INT_MAX, NULL);
333 if(*sem != NULL) return althrd_success;
334 return althrd_error;
337 void alsem_destroy(alsem_t *sem)
339 CloseHandle(*sem);
342 int alsem_post(alsem_t *sem)
344 DWORD ret = ReleaseSemaphore(*sem, 1, NULL);
345 if(ret) return althrd_success;
346 return althrd_error;
349 int alsem_wait(alsem_t *sem)
351 DWORD ret = WaitForSingleObject(*sem, INFINITE);
352 if(ret == WAIT_OBJECT_0) return althrd_success;
353 return althrd_error;
356 int alsem_trywait(alsem_t *sem)
358 DWORD ret = WaitForSingleObject(*sem, 0);
359 if(ret == WAIT_OBJECT_0) return althrd_success;
360 if(ret == WAIT_TIMEOUT) return althrd_busy;
361 return althrd_error;
365 int altss_create(altss_t *tss_id, altss_dtor_t callback)
367 DWORD key = TlsAlloc();
368 if(key == TLS_OUT_OF_INDEXES)
369 return althrd_error;
371 *tss_id = key;
372 if(callback != NULL)
373 InsertUIntMapEntry(&TlsDestructors, key, callback);
374 return althrd_success;
377 void altss_delete(altss_t tss_id)
379 RemoveUIntMapKey(&TlsDestructors, tss_id);
380 TlsFree(tss_id);
384 int altimespec_get(struct timespec *ts, int base)
386 static_assert(sizeof(FILETIME) == sizeof(ULARGE_INTEGER),
387 "Size of FILETIME does not match ULARGE_INTEGER");
388 if(base == AL_TIME_UTC)
390 union {
391 FILETIME ftime;
392 ULARGE_INTEGER ulint;
393 } systime;
394 GetSystemTimeAsFileTime(&systime.ftime);
395 /* FILETIME is in 100-nanosecond units, or 1/10th of a microsecond. */
396 ts->tv_sec = systime.ulint.QuadPart/10000000;
397 ts->tv_nsec = (systime.ulint.QuadPart%10000000) * 100;
398 return base;
401 return 0;
405 void alcall_once(alonce_flag *once, void (*callback)(void))
407 LONG ret;
408 while((ret=InterlockedExchange(once, 1)) == 1)
409 althrd_yield();
410 if(ret == 0)
411 (*callback)();
412 InterlockedExchange(once, 2);
416 void althrd_deinit(void)
418 ResetUIntMap(&ThrdIdHandle);
419 ResetUIntMap(&TlsDestructors);
422 void althrd_thread_detach(void)
424 ALsizei i;
426 LockUIntMapRead(&TlsDestructors);
427 for(i = 0;i < TlsDestructors.size;i++)
429 void *ptr = altss_get(TlsDestructors.keys[i]);
430 altss_dtor_t callback = (altss_dtor_t)TlsDestructors.values[i];
431 if(ptr)
433 if(callback) callback(ptr);
434 altss_set(TlsDestructors.keys[i], NULL);
437 UnlockUIntMapRead(&TlsDestructors);
440 #else
442 #include <sys/time.h>
443 #include <unistd.h>
444 #include <pthread.h>
445 #ifdef HAVE_PTHREAD_NP_H
446 #include <pthread_np.h>
447 #endif
450 extern inline int althrd_sleep(const struct timespec *ts, struct timespec *rem);
451 extern inline void alcall_once(alonce_flag *once, void (*callback)(void));
453 extern inline void althrd_deinit(void);
454 extern inline void althrd_thread_detach(void);
456 void althrd_setname(althrd_t thr, const char *name)
458 #if defined(HAVE_PTHREAD_SETNAME_NP)
459 #if defined(PTHREAD_SETNAME_NP_ONE_PARAM)
460 if(althrd_equal(thr, althrd_current()))
461 pthread_setname_np(name);
462 #elif defined(PTHREAD_SETNAME_NP_THREE_PARAMS)
463 pthread_setname_np(thr, "%s", (void*)name);
464 #else
465 pthread_setname_np(thr, name);
466 #endif
467 #elif defined(HAVE_PTHREAD_SET_NAME_NP)
468 pthread_set_name_np(thr, name);
469 #else
470 (void)thr;
471 (void)name;
472 #endif
476 typedef struct thread_cntr {
477 althrd_start_t func;
478 void *arg;
479 } thread_cntr;
481 static void *althrd_starter(void *arg)
483 thread_cntr cntr;
484 memcpy(&cntr, arg, sizeof(cntr));
485 free(arg);
487 return (void*)(intptr_t)((*cntr.func)(cntr.arg));
491 int althrd_create(althrd_t *thr, althrd_start_t func, void *arg)
493 thread_cntr *cntr;
494 pthread_attr_t attr;
495 size_t stackmult = 1;
496 int err;
498 cntr = malloc(sizeof(*cntr));
499 if(!cntr) return althrd_nomem;
501 if(pthread_attr_init(&attr) != 0)
503 free(cntr);
504 return althrd_error;
506 retry_stacksize:
507 if(pthread_attr_setstacksize(&attr, THREAD_STACK_SIZE*stackmult) != 0)
509 pthread_attr_destroy(&attr);
510 free(cntr);
511 return althrd_error;
514 cntr->func = func;
515 cntr->arg = arg;
516 if((err=pthread_create(thr, &attr, althrd_starter, cntr)) == 0)
518 pthread_attr_destroy(&attr);
519 return althrd_success;
522 if(err == EINVAL)
524 /* If an invalid stack size, try increasing it (limit x4, 8MB). */
525 if(stackmult < 4)
527 stackmult *= 2;
528 goto retry_stacksize;
530 /* If still nothing, try defaults and hope they're good enough. */
531 if(pthread_create(thr, NULL, althrd_starter, cntr) == 0)
533 pthread_attr_destroy(&attr);
534 return althrd_success;
537 pthread_attr_destroy(&attr);
538 free(cntr);
539 return althrd_error;
542 int althrd_detach(althrd_t thr)
544 if(pthread_detach(thr) != 0)
545 return althrd_error;
546 return althrd_success;
549 int althrd_join(althrd_t thr, int *res)
551 void *code;
553 if(pthread_join(thr, &code) != 0)
554 return althrd_error;
555 if(res != NULL)
556 *res = (int)(intptr_t)code;
557 return althrd_success;
561 int almtx_init(almtx_t *mtx, int type)
563 int ret;
565 if(!mtx) return althrd_error;
566 if((type&~almtx_recursive) != 0)
567 return althrd_error;
569 if(type == almtx_plain)
570 ret = pthread_mutex_init(mtx, NULL);
571 else
573 pthread_mutexattr_t attr;
575 ret = pthread_mutexattr_init(&attr);
576 if(ret) return althrd_error;
578 if(type == almtx_recursive)
580 ret = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
581 #ifdef HAVE_PTHREAD_MUTEXATTR_SETKIND_NP
582 if(ret != 0)
583 ret = pthread_mutexattr_setkind_np(&attr, PTHREAD_MUTEX_RECURSIVE);
584 #endif
586 else
587 ret = 1;
588 if(ret == 0)
589 ret = pthread_mutex_init(mtx, &attr);
590 pthread_mutexattr_destroy(&attr);
592 return ret ? althrd_error : althrd_success;
595 void almtx_destroy(almtx_t *mtx)
597 pthread_mutex_destroy(mtx);
600 int alcnd_init(alcnd_t *cond)
602 if(pthread_cond_init(cond, NULL) == 0)
603 return althrd_success;
604 return althrd_error;
607 int alcnd_signal(alcnd_t *cond)
609 if(pthread_cond_signal(cond) == 0)
610 return althrd_success;
611 return althrd_error;
614 int alcnd_broadcast(alcnd_t *cond)
616 if(pthread_cond_broadcast(cond) == 0)
617 return althrd_success;
618 return althrd_error;
621 int alcnd_wait(alcnd_t *cond, almtx_t *mtx)
623 if(pthread_cond_wait(cond, mtx) == 0)
624 return althrd_success;
625 return althrd_error;
628 void alcnd_destroy(alcnd_t *cond)
630 pthread_cond_destroy(cond);
634 int alsem_init(alsem_t *sem, unsigned int initial)
636 if(sem_init(sem, 0, initial) == 0)
637 return althrd_success;
638 return althrd_error;
641 void alsem_destroy(alsem_t *sem)
643 sem_destroy(sem);
646 int alsem_post(alsem_t *sem)
648 if(sem_post(sem) == 0)
649 return althrd_success;
650 return althrd_error;
653 int alsem_wait(alsem_t *sem)
655 if(sem_wait(sem) == 0) return althrd_success;
656 if(errno == EINTR) return -2;
657 return althrd_error;
660 int alsem_trywait(alsem_t *sem)
662 if(sem_trywait(sem) == 0) return althrd_success;
663 if(errno == EWOULDBLOCK) return althrd_busy;
664 if(errno == EINTR) return -2;
665 return althrd_error;
669 int altss_create(altss_t *tss_id, altss_dtor_t callback)
671 if(pthread_key_create(tss_id, callback) != 0)
672 return althrd_error;
673 return althrd_success;
676 void altss_delete(altss_t tss_id)
678 pthread_key_delete(tss_id);
682 int altimespec_get(struct timespec *ts, int base)
684 if(base == AL_TIME_UTC)
686 int ret;
687 #if _POSIX_TIMERS > 0
688 ret = clock_gettime(CLOCK_REALTIME, ts);
689 if(ret == 0) return base;
690 #else /* _POSIX_TIMERS > 0 */
691 struct timeval tv;
692 ret = gettimeofday(&tv, NULL);
693 if(ret == 0)
695 ts->tv_sec = tv.tv_sec;
696 ts->tv_nsec = tv.tv_usec * 1000;
697 return base;
699 #endif
702 return 0;
705 #endif
708 void al_nssleep(unsigned long nsec)
710 struct timespec ts, rem;
711 ts.tv_sec = nsec / 1000000000ul;
712 ts.tv_nsec = nsec % 1000000000ul;
714 while(althrd_sleep(&ts, &rem) == -1)
715 ts = rem;