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
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
);
46 #if defined(__cplusplus)
48 #elif defined(__GNUC__)
49 #define UNUSED(x) UNUSED_##x __attribute__((unused))
50 #elif defined(__LCLINT__)
51 #define UNUSED(x) /*@unused@*/ x
58 #define THREAD_STACK_SIZE (2*1024*1024) /* 2MB */
62 #define WIN32_LEAN_AND_MEAN
67 void althrd_setname(althrd_t thr
, const char *name
)
70 #define MS_VC_EXCEPTION 0x406D1388
73 DWORD dwType
; // Must be 0x1000.
74 LPCSTR szName
; // Pointer to name (in user addr space).
75 DWORD dwThreadID
; // Thread ID (-1=caller thread).
76 DWORD dwFlags
; // Reserved for future use, must be zero.
81 info
.dwThreadID
= thr
;
85 RaiseException(MS_VC_EXCEPTION
, 0, sizeof(info
)/sizeof(ULONG_PTR
), (ULONG_PTR
*)&info
);
87 __except(EXCEPTION_CONTINUE_EXECUTION
) {
89 #undef MS_VC_EXCEPTION
97 static UIntMap ThrdIdHandle
= UINTMAP_STATIC_INITIALIZE
;
99 static void NTAPI
althrd_callback(void* UNUSED(handle
), DWORD reason
, void* UNUSED(reserved
))
101 if(reason
== DLL_PROCESS_DETACH
)
102 ResetUIntMap(&ThrdIdHandle
);
105 #pragma section(".CRT$XLC",read)
106 __declspec(allocate(".CRT$XLC")) PIMAGE_TLS_CALLBACK althrd_callback_
= althrd_callback
;
107 #elif defined(__GNUC__)
108 PIMAGE_TLS_CALLBACK althrd_callback_
__attribute__((section(".CRT$XLC"))) = althrd_callback
;
110 PIMAGE_TLS_CALLBACK althrd_callback_
= althrd_callback
;
114 typedef struct thread_cntr
{
119 static DWORD WINAPI
althrd_starter(void *arg
)
122 memcpy(&cntr
, arg
, sizeof(cntr
));
125 return (DWORD
)((*cntr
.func
)(cntr
.arg
));
129 int althrd_create(althrd_t
*thr
, althrd_start_t func
, void *arg
)
135 cntr
= malloc(sizeof(*cntr
));
136 if(!cntr
) return althrd_nomem
;
141 hdl
= CreateThread(NULL
, THREAD_STACK_SIZE
, althrd_starter
, cntr
, 0, &thrid
);
147 InsertUIntMapEntry(&ThrdIdHandle
, thrid
, hdl
);
150 return althrd_success
;
153 int althrd_detach(althrd_t thr
)
155 HANDLE hdl
= RemoveUIntMapKey(&ThrdIdHandle
, thr
);
156 if(!hdl
) return althrd_error
;
159 return althrd_success
;
162 int althrd_join(althrd_t thr
, int *res
)
166 HANDLE hdl
= RemoveUIntMapKey(&ThrdIdHandle
, thr
);
167 if(!hdl
) return althrd_error
;
169 WaitForSingleObject(hdl
, INFINITE
);
170 GetExitCodeThread(hdl
, &code
);
175 return althrd_success
;
178 int althrd_sleep(const struct timespec
*ts
, struct timespec
* UNUSED(rem
))
182 if(ts
->tv_sec
< 0 || ts
->tv_sec
>= (0x7fffffff / 1000) ||
183 ts
->tv_nsec
< 0 || ts
->tv_nsec
>= 1000000000)
186 msec
= (DWORD
)(ts
->tv_sec
* 1000);
187 msec
+= (DWORD
)((ts
->tv_nsec
+999999) / 1000000);
194 int almtx_init(almtx_t
*mtx
, int type
)
196 if(!mtx
) return althrd_error
;
198 type
&= ~almtx_recursive
;
199 if(type
!= almtx_plain
)
202 InitializeCriticalSection(mtx
);
203 return althrd_success
;
206 void almtx_destroy(almtx_t
*mtx
)
208 DeleteCriticalSection(mtx
);
211 int almtx_timedlock(almtx_t
* UNUSED(mtx
), const struct timespec
* UNUSED(ts
))
213 /* Windows CRITICAL_SECTIONs don't seem to have a timedlock method. */
217 #if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0600
218 int alcnd_init(alcnd_t
*cond
)
220 InitializeConditionVariable(cond
);
221 return althrd_success
;
224 int alcnd_signal(alcnd_t
*cond
)
226 WakeConditionVariable(cond
);
227 return althrd_success
;
230 int alcnd_broadcast(alcnd_t
*cond
)
232 WakeAllConditionVariable(cond
);
233 return althrd_success
;
236 int alcnd_wait(alcnd_t
*cond
, almtx_t
*mtx
)
238 if(SleepConditionVariableCS(cond
, mtx
, INFINITE
) != 0)
239 return althrd_success
;
243 int alcnd_timedwait(alcnd_t
*cond
, almtx_t
*mtx
, const struct timespec
*time_point
)
245 struct timespec curtime
;
248 if(altimespec_get(&curtime
, AL_TIME_UTC
) != AL_TIME_UTC
)
251 if(curtime
.tv_sec
> time_point
->tv_sec
|| (curtime
.tv_sec
== time_point
->tv_sec
&&
252 curtime
.tv_nsec
>= time_point
->tv_nsec
))
254 if(SleepConditionVariableCS(cond
, mtx
, 0) != 0)
255 return althrd_success
;
259 sleeptime
= (time_point
->tv_nsec
- curtime
.tv_nsec
+ 999999)/1000000;
260 sleeptime
+= (DWORD
)(time_point
->tv_sec
- curtime
.tv_sec
)*1000;
261 if(SleepConditionVariableCS(cond
, mtx
, sleeptime
) != 0)
262 return althrd_success
;
264 return (GetLastError()==ERROR_TIMEOUT
) ? althrd_timedout
: althrd_error
;
267 void alcnd_destroy(alcnd_t
* UNUSED(cond
))
269 /* Nothing to delete? */
274 /* WARNING: This is a rather poor implementation of condition variables, with
275 * known problems. However, it's simple, efficient, and good enough for now to
276 * not require Vista. Based on "Strategies for Implementing POSIX Condition
277 * Variables" by Douglas C. Schmidt and Irfan Pyarali:
278 * http://www.cs.wustl.edu/~schmidt/win32-cv-1.html
280 /* A better solution may be using Wine's implementation. It requires internals
281 * (NtCreateKeyedEvent, NtReleaseKeyedEvent, and NtWaitForKeyedEvent) from
282 * ntdll, and implemention of exchange and compare-exchange for RefCounts.
295 int alcnd_init(alcnd_t
*cond
)
297 _int_alcnd_t
*icond
= calloc(1, sizeof(*icond
));
298 if(!icond
) return althrd_nomem
;
300 InitRef(&icond
->wait_count
, 0);
302 icond
->events
[SIGNAL
] = CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
303 icond
->events
[BROADCAST
] = CreateEventW(NULL
, TRUE
, FALSE
, NULL
);
304 if(!icond
->events
[SIGNAL
] || !icond
->events
[BROADCAST
])
306 if(icond
->events
[SIGNAL
])
307 CloseHandle(icond
->events
[SIGNAL
]);
308 if(icond
->events
[BROADCAST
])
309 CloseHandle(icond
->events
[BROADCAST
]);
315 return althrd_success
;
318 int alcnd_signal(alcnd_t
*cond
)
320 _int_alcnd_t
*icond
= cond
->Ptr
;
321 if(ReadRef(&icond
->wait_count
) > 0)
322 SetEvent(icond
->events
[SIGNAL
]);
323 return althrd_success
;
326 int alcnd_broadcast(alcnd_t
*cond
)
328 _int_alcnd_t
*icond
= cond
->Ptr
;
329 if(ReadRef(&icond
->wait_count
) > 0)
330 SetEvent(icond
->events
[BROADCAST
]);
331 return althrd_success
;
334 int alcnd_wait(alcnd_t
*cond
, almtx_t
*mtx
)
336 _int_alcnd_t
*icond
= cond
->Ptr
;
339 IncrementRef(&icond
->wait_count
);
340 LeaveCriticalSection(mtx
);
342 res
= WaitForMultipleObjects(2, icond
->events
, FALSE
, INFINITE
);
344 if(DecrementRef(&icond
->wait_count
) == 0 && res
== WAIT_OBJECT_0
+BROADCAST
)
345 ResetEvent(icond
->events
[BROADCAST
]);
346 EnterCriticalSection(mtx
);
348 return althrd_success
;
351 int alcnd_timedwait(alcnd_t
*cond
, almtx_t
*mtx
, const struct timespec
*time_point
)
353 _int_alcnd_t
*icond
= cond
->Ptr
;
354 struct timespec curtime
;
358 if(altimespec_get(&curtime
, AL_TIME_UTC
) != AL_TIME_UTC
)
361 if(curtime
.tv_sec
> time_point
->tv_sec
|| (curtime
.tv_sec
== time_point
->tv_sec
&&
362 curtime
.tv_nsec
>= time_point
->tv_nsec
))
366 sleeptime
= (time_point
->tv_nsec
- curtime
.tv_nsec
+ 999999)/1000000;
367 sleeptime
+= (DWORD
)(time_point
->tv_sec
- curtime
.tv_sec
)*1000;
370 IncrementRef(&icond
->wait_count
);
371 LeaveCriticalSection(mtx
);
373 res
= WaitForMultipleObjects(2, icond
->events
, FALSE
, sleeptime
);
375 if(DecrementRef(&icond
->wait_count
) == 0 && res
== WAIT_OBJECT_0
+BROADCAST
)
376 ResetEvent(icond
->events
[BROADCAST
]);
377 EnterCriticalSection(mtx
);
379 return (res
== WAIT_TIMEOUT
) ? althrd_timedout
: althrd_success
;
382 void alcnd_destroy(alcnd_t
*cond
)
384 _int_alcnd_t
*icond
= cond
->Ptr
;
385 CloseHandle(icond
->events
[SIGNAL
]);
386 CloseHandle(icond
->events
[BROADCAST
]);
389 #endif /* defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0600 */
392 /* An associative map of uint:void* pairs. The key is the TLS index (given by
393 * TlsAlloc), and the value is the altss_dtor_t callback. When a thread exits,
394 * we iterate over the TLS indices for their thread-local value and call the
395 * destructor function with it if they're both not NULL. To avoid using
396 * DllMain, a PIMAGE_TLS_CALLBACK function pointer is placed in a ".CRT$XLx"
397 * section (where x is a character A to Z) which will be called by the CRT.
399 static UIntMap TlsDestructors
= UINTMAP_STATIC_INITIALIZE
;
401 static void NTAPI
altss_callback(void* UNUSED(handle
), DWORD reason
, void* UNUSED(reserved
))
405 if(reason
== DLL_PROCESS_DETACH
)
407 ResetUIntMap(&TlsDestructors
);
410 if(reason
!= DLL_THREAD_DETACH
)
413 LockUIntMapRead(&TlsDestructors
);
414 for(i
= 0;i
< TlsDestructors
.size
;i
++)
416 void *ptr
= altss_get(TlsDestructors
.keys
[i
]);
417 altss_dtor_t callback
= (altss_dtor_t
)TlsDestructors
.values
[i
];
421 UnlockUIntMapRead(&TlsDestructors
);
424 #pragma section(".CRT$XLB",read)
425 __declspec(allocate(".CRT$XLB")) PIMAGE_TLS_CALLBACK altss_callback_
= altss_callback
;
426 #elif defined(__GNUC__)
427 PIMAGE_TLS_CALLBACK altss_callback_
__attribute__((section(".CRT$XLB"))) = altss_callback
;
429 #warning "No TLS callback support, thread-local contexts may leak references on poorly written applications."
430 PIMAGE_TLS_CALLBACK altss_callback_
= altss_callback
;
433 int altss_create(altss_t
*tss_id
, altss_dtor_t callback
)
435 DWORD key
= TlsAlloc();
436 if(key
== TLS_OUT_OF_INDEXES
)
441 InsertUIntMapEntry(&TlsDestructors
, key
, callback
);
442 return althrd_success
;
445 void altss_delete(altss_t tss_id
)
447 RemoveUIntMapKey(&TlsDestructors
, tss_id
);
452 int altimespec_get(struct timespec
*ts
, int base
)
454 static_assert(sizeof(FILETIME
) == sizeof(ULARGE_INTEGER
),
455 "Size of FILETIME does not match ULARGE_INTEGER");
456 if(base
== AL_TIME_UTC
)
460 ULARGE_INTEGER ulint
;
462 GetSystemTimeAsFileTime(&systime
.ftime
);
463 /* FILETIME is in 100-nanosecond units, or 1/10th of a microsecond. */
464 ts
->tv_sec
= systime
.ulint
.QuadPart
/10000000;
465 ts
->tv_nsec
= (systime
.ulint
.QuadPart
%10000000) * 100;
473 void alcall_once(alonce_flag
*once
, void (*callback
)(void))
476 while((ret
=InterlockedExchange(once
, 1)) == 1)
480 InterlockedExchange(once
, 2);
485 #include <sys/time.h>
488 #ifdef HAVE_PTHREAD_NP_H
489 #include <pthread_np.h>
493 extern inline int althrd_sleep(const struct timespec
*ts
, struct timespec
*rem
);
494 extern inline void alcall_once(alonce_flag
*once
, void (*callback
)(void));
497 void althrd_setname(althrd_t thr
, const char *name
)
499 #if defined(HAVE_PTHREAD_SETNAME_NP)
500 #if defined(PTHREAD_SETNAME_NP_ONE_PARAM)
501 if(althrd_equal(thr
, althrd_current()))
502 pthread_setname_np(name
);
504 pthread_setname_np(thr
, name
);
506 #elif defined(HAVE_PTHREAD_SET_NAME_NP)
507 pthread_set_name_np(thr
, name
);
515 typedef struct thread_cntr
{
520 static void *althrd_starter(void *arg
)
523 memcpy(&cntr
, arg
, sizeof(cntr
));
526 return (void*)(intptr_t)((*cntr
.func
)(cntr
.arg
));
530 int althrd_create(althrd_t
*thr
, althrd_start_t func
, void *arg
)
534 size_t stackmult
= 1;
537 cntr
= malloc(sizeof(*cntr
));
538 if(!cntr
) return althrd_nomem
;
540 if(pthread_attr_init(&attr
) != 0)
546 if(pthread_attr_setstacksize(&attr
, THREAD_STACK_SIZE
*stackmult
) != 0)
548 pthread_attr_destroy(&attr
);
555 if((err
=pthread_create(thr
, &attr
, althrd_starter
, cntr
)) == 0)
557 pthread_attr_destroy(&attr
);
558 return althrd_success
;
563 /* If an invalid stack size, try increasing it (limit x4, 8MB). */
567 goto retry_stacksize
;
569 /* If still nothing, try defaults and hope they're good enough. */
570 if(pthread_create(thr
, NULL
, althrd_starter
, cntr
) == 0)
572 pthread_attr_destroy(&attr
);
573 return althrd_success
;
576 pthread_attr_destroy(&attr
);
581 int althrd_detach(althrd_t thr
)
583 if(pthread_detach(thr
) != 0)
585 return althrd_success
;
588 int althrd_join(althrd_t thr
, int *res
)
592 if(pthread_join(thr
, &code
) != 0)
595 *res
= (int)(intptr_t)code
;
596 return althrd_success
;
600 int almtx_init(almtx_t
*mtx
, int type
)
604 if(!mtx
) return althrd_error
;
605 #ifdef HAVE_PTHREAD_MUTEX_TIMEDLOCK
606 if((type
&~(almtx_recursive
|almtx_timed
)) != 0)
609 if((type
&~almtx_recursive
) != 0)
613 type
&= ~almtx_timed
;
614 if(type
== almtx_plain
)
615 ret
= pthread_mutex_init(mtx
, NULL
);
618 pthread_mutexattr_t attr
;
620 ret
= pthread_mutexattr_init(&attr
);
621 if(ret
) return althrd_error
;
623 if(type
== almtx_recursive
)
625 ret
= pthread_mutexattr_settype(&attr
, PTHREAD_MUTEX_RECURSIVE
);
626 #ifdef HAVE_PTHREAD_MUTEXATTR_SETKIND_NP
628 ret
= pthread_mutexattr_setkind_np(&attr
, PTHREAD_MUTEX_RECURSIVE
);
634 ret
= pthread_mutex_init(mtx
, &attr
);
635 pthread_mutexattr_destroy(&attr
);
637 return ret
? althrd_error
: althrd_success
;
640 void almtx_destroy(almtx_t
*mtx
)
642 pthread_mutex_destroy(mtx
);
645 int almtx_timedlock(almtx_t
*mtx
, const struct timespec
*ts
)
647 #ifdef HAVE_PTHREAD_MUTEX_TIMEDLOCK
648 int ret
= pthread_mutex_timedlock(mtx
, ts
);
651 case 0: return althrd_success
;
652 case ETIMEDOUT
: return althrd_timedout
;
653 case EBUSY
: return althrd_busy
;
659 int alcnd_init(alcnd_t
*cond
)
661 if(pthread_cond_init(cond
, NULL
) == 0)
662 return althrd_success
;
666 int alcnd_signal(alcnd_t
*cond
)
668 if(pthread_cond_signal(cond
) == 0)
669 return althrd_success
;
673 int alcnd_broadcast(alcnd_t
*cond
)
675 if(pthread_cond_broadcast(cond
) == 0)
676 return althrd_success
;
680 int alcnd_wait(alcnd_t
*cond
, almtx_t
*mtx
)
682 if(pthread_cond_wait(cond
, mtx
) == 0)
683 return althrd_success
;
687 int alcnd_timedwait(alcnd_t
*cond
, almtx_t
*mtx
, const struct timespec
*time_point
)
689 if(pthread_cond_timedwait(cond
, mtx
, time_point
) == 0)
690 return althrd_success
;
694 void alcnd_destroy(alcnd_t
*cond
)
696 pthread_cond_destroy(cond
);
700 int altss_create(altss_t
*tss_id
, altss_dtor_t callback
)
702 if(pthread_key_create(tss_id
, callback
) != 0)
704 return althrd_success
;
707 void altss_delete(altss_t tss_id
)
709 pthread_key_delete(tss_id
);
713 int altimespec_get(struct timespec
*ts
, int base
)
715 if(base
== AL_TIME_UTC
)
718 #if _POSIX_TIMERS > 0
719 ret
= clock_gettime(CLOCK_REALTIME
, ts
);
720 if(ret
== 0) return base
;
721 #else /* _POSIX_TIMERS > 0 */
723 ret
= gettimeofday(&tv
, NULL
);
726 ts
->tv_sec
= tv
.tv_sec
;
727 ts
->tv_nsec
= tv
.tv_usec
* 1000;
739 void al_nssleep(unsigned long nsec
)
741 struct timespec ts
, rem
;
742 ts
.tv_sec
= nsec
/ 1000000000ul;
743 ts
.tv_nsec
= nsec
% 1000000000ul;
745 while(althrd_sleep(&ts
, &rem
) == -1)