Move some headers to include/
[openal-soft.git] / Alc / threads.c
blob06036685932576f187bc9cc9d67d36f1d8322aa9
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., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, 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 <errno.h>
28 #include "alMain.h"
29 #include "alThunk.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 #define THREAD_STACK_SIZE (1*1024*1024) /* 1MB */
47 #ifdef _WIN32
49 #define WIN32_LEAN_AND_MEAN
50 #include <windows.h>
51 #include <mmsystem.h>
54 void althrd_setname(althrd_t thr, const char *name)
56 #if defined(_MSC_VER)
57 #define MS_VC_EXCEPTION 0x406D1388
58 #pragma pack(push,8)
59 struct {
60 DWORD dwType; // Must be 0x1000.
61 LPCSTR szName; // Pointer to name (in user addr space).
62 DWORD dwThreadID; // Thread ID (-1=caller thread).
63 DWORD dwFlags; // Reserved for future use, must be zero.
64 } info;
65 #pragma pack(pop)
66 info.dwType = 0x1000;
67 info.szName = name;
68 info.dwThreadID = thr;
69 info.dwFlags = 0;
71 __try {
72 RaiseException(MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info);
74 __except(EXCEPTION_CONTINUE_EXECUTION) {
76 #undef MS_VC_EXCEPTION
77 #else
78 TRACE("Can't set thread %04lx name to \"%s\"\n", thr, name);
79 #endif
83 static UIntMap ThrdIdHandle = UINTMAP_STATIC_INITIALIZE;
85 static void NTAPI althrd_callback(void* UNUSED(handle), DWORD reason, void* UNUSED(reserved))
87 if(reason == DLL_PROCESS_DETACH)
88 ResetUIntMap(&ThrdIdHandle);
90 #ifdef _MSC_VER
91 #pragma section(".CRT$XLC",read)
92 __declspec(allocate(".CRT$XLC")) PIMAGE_TLS_CALLBACK althrd_callback_ = althrd_callback;
93 #elif defined(__GNUC__)
94 PIMAGE_TLS_CALLBACK althrd_callback_ __attribute__((section(".CRT$XLC"))) = althrd_callback;
95 #else
96 PIMAGE_TLS_CALLBACK althrd_callback_ = althrd_callback;
97 #endif
100 typedef struct thread_cntr {
101 althrd_start_t func;
102 void *arg;
103 } thread_cntr;
105 static DWORD WINAPI althrd_starter(void *arg)
107 thread_cntr cntr;
108 memcpy(&cntr, arg, sizeof(cntr));
109 free(arg);
111 return (DWORD)((*cntr.func)(cntr.arg));
115 int althrd_create(althrd_t *thr, althrd_start_t func, void *arg)
117 thread_cntr *cntr;
118 DWORD thrid;
119 HANDLE hdl;
121 cntr = malloc(sizeof(*cntr));
122 if(!cntr) return althrd_nomem;
124 cntr->func = func;
125 cntr->arg = arg;
127 hdl = CreateThread(NULL, THREAD_STACK_SIZE, althrd_starter, cntr, 0, &thrid);
128 if(!hdl)
130 free(cntr);
131 return althrd_error;
133 InsertUIntMapEntry(&ThrdIdHandle, thrid, hdl);
135 *thr = thrid;
136 return althrd_success;
139 int althrd_detach(althrd_t thr)
141 HANDLE hdl = RemoveUIntMapKey(&ThrdIdHandle, thr);
142 if(!hdl) return althrd_error;
144 CloseHandle(hdl);
145 return althrd_success;
148 int althrd_join(althrd_t thr, int *res)
150 DWORD code;
152 HANDLE hdl = RemoveUIntMapKey(&ThrdIdHandle, thr);
153 if(!hdl) return althrd_error;
155 WaitForSingleObject(hdl, INFINITE);
156 GetExitCodeThread(hdl, &code);
157 CloseHandle(hdl);
159 *res = (int)code;
160 return althrd_success;
163 int althrd_sleep(const struct timespec *ts, struct timespec* UNUSED(rem))
165 DWORD msec;
167 if(ts->tv_sec < 0 || ts->tv_sec >= (0x7fffffff / 1000) ||
168 ts->tv_nsec < 0 || ts->tv_nsec >= 1000000000)
169 return -2;
171 msec = (DWORD)(ts->tv_sec * 1000);
172 msec += (DWORD)((ts->tv_nsec+999999) / 1000000);
173 Sleep(msec);
175 return 0;
179 int almtx_init(almtx_t *mtx, int type)
181 if(!mtx) return althrd_error;
182 type &= ~(almtx_recursive|almtx_timed);
183 if(type != almtx_plain)
184 return althrd_error;
186 InitializeCriticalSection(mtx);
187 return althrd_success;
190 void almtx_destroy(almtx_t *mtx)
192 DeleteCriticalSection(mtx);
195 int almtx_timedlock(almtx_t *mtx, const struct timespec *ts)
197 int ret;
199 if(!mtx || !ts)
200 return althrd_error;
202 while((ret=almtx_trylock(mtx)) == althrd_busy)
204 struct timespec now;
206 if(ts->tv_sec < 0 || ts->tv_nsec < 0 || ts->tv_nsec >= 1000000000 ||
207 altimespec_get(&now, AL_TIME_UTC) != AL_TIME_UTC)
208 return althrd_error;
209 if(now.tv_sec > ts->tv_sec || (now.tv_sec == ts->tv_sec && now.tv_nsec >= ts->tv_nsec))
210 return althrd_timedout;
212 althrd_yield();
215 return ret;
219 /* An associative map of uint:void* pairs. The key is the TLS index (given by
220 * TlsAlloc), and the value is the altss_dtor_t callback. When a thread exits,
221 * we iterate over the TLS indices for their thread-local value and call the
222 * destructor function with it if they're both not NULL. To avoid using
223 * DllMain, a PIMAGE_TLS_CALLBACK function pointer is placed in a ".CRT$XLx"
224 * section (where x is a character A to Z) which will be called by the CRT.
226 static UIntMap TlsDestructors = UINTMAP_STATIC_INITIALIZE;
228 static void NTAPI altss_callback(void* UNUSED(handle), DWORD reason, void* UNUSED(reserved))
230 ALsizei i;
232 if(reason == DLL_PROCESS_DETACH)
234 ResetUIntMap(&TlsDestructors);
235 return;
237 if(reason != DLL_THREAD_DETACH)
238 return;
240 LockUIntMapRead(&TlsDestructors);
241 for(i = 0;i < TlsDestructors.size;i++)
243 void *ptr = altss_get(TlsDestructors.array[i].key);
244 altss_dtor_t callback = (altss_dtor_t)TlsDestructors.array[i].value;
245 if(ptr && callback)
246 callback(ptr);
248 UnlockUIntMapRead(&TlsDestructors);
250 #ifdef _MSC_VER
251 #pragma section(".CRT$XLB",read)
252 __declspec(allocate(".CRT$XLB")) PIMAGE_TLS_CALLBACK altss_callback_ = altss_callback;
253 #elif defined(__GNUC__)
254 PIMAGE_TLS_CALLBACK altss_callback_ __attribute__((section(".CRT$XLB"))) = altss_callback;
255 #else
256 #warning "No TLS callback support, thread-local contexts may leak references on poorly written applications."
257 PIMAGE_TLS_CALLBACK altss_callback_ = altss_callback;
258 #endif
260 int altss_create(altss_t *tss_id, altss_dtor_t callback)
262 DWORD key = TlsAlloc();
263 if(key == TLS_OUT_OF_INDEXES)
264 return althrd_error;
266 *tss_id = key;
267 if(callback != NULL)
268 InsertUIntMapEntry(&TlsDestructors, key, callback);
269 return althrd_success;
272 void altss_delete(altss_t tss_id)
274 RemoveUIntMapKey(&TlsDestructors, tss_id);
275 TlsFree(tss_id);
279 int altimespec_get(struct timespec *ts, int base)
281 if(base == AL_TIME_UTC)
283 union {
284 FILETIME ftime;
285 ULARGE_INTEGER ulint;
286 } systime;
287 GetSystemTimeAsFileTime(&systime.ftime);
288 /* FILETIME is in 100-nanosecond units, or 1/10th of a microsecond. */
289 ts->tv_sec = systime.ulint.QuadPart/10000000;
290 ts->tv_nsec = (systime.ulint.QuadPart%10000000) * 100;
291 return base;
294 return 0;
298 void alcall_once(alonce_flag *once, void (*callback)(void))
300 LONG ret;
301 while((ret=InterlockedExchange(once, 1)) == 1)
302 althrd_yield();
303 if(ret == 0)
304 (*callback)();
305 InterlockedExchange(once, 2);
308 #else
310 #include <sys/time.h>
311 #include <unistd.h>
312 #include <pthread.h>
313 #ifdef HAVE_PTHREAD_NP_H
314 #include <pthread_np.h>
315 #endif
318 extern inline int althrd_sleep(const struct timespec *ts, struct timespec *rem);
319 extern inline void alcall_once(alonce_flag *once, void (*callback)(void));
322 void althrd_setname(althrd_t thr, const char *name)
324 #if defined(HAVE_PTHREAD_SETNAME_NP)
325 #if defined(__GNUC__)
326 if(pthread_setname_np(thr, name) != 0)
327 #elif defined(__APPLE__)
328 if(!althrd_equal(thr, althrd_current())
329 WARN("Can't set thread name \"%s\" on non-current thread");
330 else if(pthread_setname_np(name) != 0)
331 #endif
332 WARN("Failed to set thread name to \"%s\": %s\n", name, strerror(errno));
333 #elif defined(HAVE_PTHREAD_SET_NAME_NP)
334 pthread_set_name_np(thr, name);
335 #else
336 TRACE("Can't set thread name to \"%s\"\n", name);
337 #endif
341 typedef struct thread_cntr {
342 althrd_start_t func;
343 void *arg;
344 } thread_cntr;
346 static void *althrd_starter(void *arg)
348 thread_cntr cntr;
349 memcpy(&cntr, arg, sizeof(cntr));
350 free(arg);
352 return (void*)(intptr_t)((*cntr.func)(cntr.arg));
356 int althrd_create(althrd_t *thr, althrd_start_t func, void *arg)
358 thread_cntr *cntr;
359 pthread_attr_t attr;
361 cntr = malloc(sizeof(*cntr));
362 if(!cntr) return althrd_nomem;
364 if(pthread_attr_init(&attr) != 0)
366 free(cntr);
367 return althrd_error;
369 if(pthread_attr_setstacksize(&attr, THREAD_STACK_SIZE) != 0)
371 pthread_attr_destroy(&attr);
372 free(cntr);
373 return althrd_error;
376 cntr->func = func;
377 cntr->arg = arg;
378 if(pthread_create(thr, &attr, althrd_starter, cntr) != 0)
380 pthread_attr_destroy(&attr);
381 free(cntr);
382 return althrd_error;
384 pthread_attr_destroy(&attr);
386 return althrd_success;
389 int althrd_detach(althrd_t thr)
391 if(pthread_detach(thr) != 0)
392 return althrd_error;
393 return althrd_success;
396 int althrd_join(althrd_t thr, int *res)
398 void *code;
400 if(!res) return althrd_error;
402 if(pthread_join(thr, &code) != 0)
403 return althrd_error;
404 *res = (int)(intptr_t)code;
405 return althrd_success;
409 int almtx_init(almtx_t *mtx, int type)
411 int ret;
413 if(!mtx) return althrd_error;
414 if((type&~(almtx_recursive|almtx_timed)) != 0)
415 return althrd_error;
417 type &= ~almtx_timed;
418 if(type == almtx_plain)
419 ret = pthread_mutex_init(mtx, NULL);
420 else
422 pthread_mutexattr_t attr;
424 ret = pthread_mutexattr_init(&attr);
425 if(ret) return althrd_error;
427 if(type == almtx_recursive)
429 ret = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
430 #ifdef HAVE_PTHREAD_MUTEXATTR_SETKIND_NP
431 if(ret != 0)
432 ret = pthread_mutexattr_setkind_np(&attr, PTHREAD_MUTEX_RECURSIVE);
433 #endif
435 else
436 ret = 1;
437 if(ret == 0)
438 ret = pthread_mutex_init(mtx, &attr);
439 pthread_mutexattr_destroy(&attr);
441 return ret ? althrd_error : althrd_success;
444 void almtx_destroy(almtx_t *mtx)
446 pthread_mutex_destroy(mtx);
449 int almtx_timedlock(almtx_t *mtx, const struct timespec *ts)
451 int ret;
453 #ifdef HAVE_PTHREAD_MUTEX_TIMEDLOCK
454 ret = pthread_mutex_timedlock(mtx, ts);
455 switch(ret)
457 case 0: return althrd_success;
458 case ETIMEDOUT: return althrd_timedout;
459 case EBUSY: return althrd_busy;
461 return althrd_error;
462 #else
463 if(!mtx || !ts)
464 return althrd_error;
466 while((ret=almtx_trylock(mtx)) == althrd_busy)
468 struct timespec now;
470 if(ts->tv_sec < 0 || ts->tv_nsec < 0 || ts->tv_nsec >= 1000000000 ||
471 altimespec_get(&now, AL_TIME_UTC) != AL_TIME_UTC)
472 return althrd_error;
473 if(now.tv_sec > ts->tv_sec || (now.tv_sec == ts->tv_sec && now.tv_nsec >= ts->tv_nsec))
474 return althrd_timedout;
476 althrd_yield();
479 return ret;
480 #endif
484 int altss_create(altss_t *tss_id, altss_dtor_t callback)
486 if(pthread_key_create(tss_id, callback) != 0)
487 return althrd_error;
488 return althrd_success;
491 void altss_delete(altss_t tss_id)
493 pthread_key_delete(tss_id);
497 int altimespec_get(struct timespec *ts, int base)
499 if(base == AL_TIME_UTC)
501 int ret;
502 #if _POSIX_TIMERS > 0
503 ret = clock_gettime(CLOCK_REALTIME, ts);
504 if(ret == 0) return base;
505 #else /* _POSIX_TIMERS > 0 */
506 struct timeval tv;
507 ret = gettimeofday(&tv, NULL);
508 if(ret == 0)
510 ts->tv_sec = tv.tv_sec;
511 ts->tv_nsec = tv.tv_usec * 1000;
512 return base;
514 #endif
517 return 0;
520 #endif
523 void al_nssleep(time_t sec, long nsec)
525 struct timespec ts, rem;
526 ts.tv_sec = sec;
527 ts.tv_nsec = nsec;
529 while(althrd_sleep(&ts, &rem) == -1)
530 ts = rem;