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 (1*1024*1024) /* 1MB */
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
;
197 type
&= ~(almtx_recursive
|almtx_timed
);
198 if(type
!= almtx_plain
)
201 InitializeCriticalSection(mtx
);
202 return althrd_success
;
205 void almtx_destroy(almtx_t
*mtx
)
207 DeleteCriticalSection(mtx
);
210 int almtx_timedlock(almtx_t
*mtx
, const struct timespec
*ts
)
217 while((ret
=almtx_trylock(mtx
)) == althrd_busy
)
221 if(ts
->tv_sec
< 0 || ts
->tv_nsec
< 0 || ts
->tv_nsec
>= 1000000000 ||
222 altimespec_get(&now
, AL_TIME_UTC
) != AL_TIME_UTC
)
224 if(now
.tv_sec
> ts
->tv_sec
|| (now
.tv_sec
== ts
->tv_sec
&& now
.tv_nsec
>= ts
->tv_nsec
))
225 return althrd_timedout
;
233 #if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0600
234 int alcnd_init(alcnd_t
*cond
)
236 InitializeConditionVariable(cond
);
237 return althrd_success
;
240 int alcnd_signal(alcnd_t
*cond
)
242 WakeConditionVariable(cond
);
243 return althrd_success
;
246 int alcnd_broadcast(alcnd_t
*cond
)
248 WakeAllConditionVariable(cond
);
249 return althrd_success
;
252 int alcnd_wait(alcnd_t
*cond
, almtx_t
*mtx
)
254 if(SleepConditionVariableCS(cond
, mtx
, INFINITE
) != 0)
255 return althrd_success
;
259 int alcnd_timedwait(alcnd_t
*cond
, almtx_t
*mtx
, const struct timespec
*time_point
)
261 struct timespec curtime
;
264 if(altimespec_get(&curtime
, AL_TIME_UTC
) != AL_TIME_UTC
)
267 sleeptime
= (time_point
->tv_nsec
- curtime
.tv_nsec
+ 999999)/1000000;
268 sleeptime
+= (time_point
->tv_sec
- curtime
.tv_sec
)*1000;
269 if(SleepConditionVariableCS(cond
, mtx
, sleeptime
) != 0)
270 return althrd_success
;
271 return (GetLastError()==ERROR_TIMEOUT
) ? althrd_timedout
: althrd_error
;
274 void alcnd_destroy(alcnd_t
* UNUSED(cond
))
276 /* Nothing to delete? */
281 /* WARNING: This is a rather poor implementation of condition variables, with
282 * known problems. However, it's simple, efficient, and good enough for now to
283 * not require Vista. Based on "Strategies for Implementing POSIX Condition
284 * Variables" by Douglas C. Schmidt and Irfan Pyarali:
285 * http://www.cs.wustl.edu/~schmidt/win32-cv-1.html
287 /* A better solution may be using Wine's implementation. It requires internals
288 * (NtCreateKeyedEvent, NtReleaseKeyedEvent, and NtWaitForKeyedEvent) from
289 * ntdll, and implemention of exchange and compare-exchange for RefCounts.
302 int alcnd_init(alcnd_t
*cond
)
304 _int_alcnd_t
*icond
= calloc(1, sizeof(*icond
));
305 if(!icond
) return althrd_nomem
;
307 InitRef(&icond
->wait_count
, 0);
309 icond
->events
[SIGNAL
] = CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
310 icond
->events
[BROADCAST
] = CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
311 if(!icond
->events
[SIGNAL
] || !icond
->events
[BROADCAST
])
313 if(icond
->events
[SIGNAL
])
314 CloseHandle(icond
->events
[SIGNAL
]);
315 if(icond
->events
[BROADCAST
])
316 CloseHandle(icond
->events
[BROADCAST
]);
322 return althrd_success
;
325 int alcnd_signal(alcnd_t
*cond
)
327 _int_alcnd_t
*icond
= cond
->Ptr
;
328 if(ReadRef(&icond
->wait_count
) > 0)
329 SetEvent(icond
->events
[SIGNAL
]);
330 return althrd_success
;
333 int alcnd_broadcast(alcnd_t
*cond
)
335 _int_alcnd_t
*icond
= cond
->Ptr
;
336 if(ReadRef(&icond
->wait_count
) > 0)
337 SetEvent(icond
->events
[BROADCAST
]);
338 return althrd_success
;
341 int alcnd_wait(alcnd_t
*cond
, almtx_t
*mtx
)
343 _int_alcnd_t
*icond
= cond
->Ptr
;
346 IncrementRef(&icond
->wait_count
);
347 LeaveCriticalSection(mtx
);
349 res
= WaitForMultipleObjects(2, icond
->events
, FALSE
, INFINITE
);
351 if(DecrementRef(&icond
->wait_count
) == 0 && res
== WAIT_OBJECT_0
+BROADCAST
)
352 ResetEvent(icond
->events
[BROADCAST
]);
353 EnterCriticalSection(mtx
);
355 return althrd_success
;
358 int alcnd_timedwait(alcnd_t
*cond
, almtx_t
*mtx
, const struct timespec
*time_point
)
360 _int_alcnd_t
*icond
= cond
->Ptr
;
361 struct timespec curtime
;
365 if(altimespec_get(&curtime
, AL_TIME_UTC
) != AL_TIME_UTC
)
367 sleeptime
= (time_point
->tv_nsec
- curtime
.tv_nsec
+ 999999)/1000000;
368 sleeptime
+= (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
.array
[i
].key
);
417 altss_dtor_t callback
= (altss_dtor_t
)TlsDestructors
.array
[i
].value
;
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(__GNUC__)
501 pthread_setname_np(thr
, name
);
502 #elif defined(__APPLE__)
503 if(althrd_equal(thr
, althrd_current())
504 pthread_setname_np(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
)
535 cntr
= malloc(sizeof(*cntr
));
536 if(!cntr
) return althrd_nomem
;
538 if(pthread_attr_init(&attr
) != 0)
543 if(pthread_attr_setstacksize(&attr
, THREAD_STACK_SIZE
) != 0)
545 pthread_attr_destroy(&attr
);
552 if(pthread_create(thr
, &attr
, althrd_starter
, cntr
) != 0)
554 pthread_attr_destroy(&attr
);
558 pthread_attr_destroy(&attr
);
560 return althrd_success
;
563 int althrd_detach(althrd_t thr
)
565 if(pthread_detach(thr
) != 0)
567 return althrd_success
;
570 int althrd_join(althrd_t thr
, int *res
)
574 if(pthread_join(thr
, &code
) != 0)
577 *res
= (int)(intptr_t)code
;
578 return althrd_success
;
582 int almtx_init(almtx_t
*mtx
, int type
)
586 if(!mtx
) return althrd_error
;
587 if((type
&~(almtx_recursive
|almtx_timed
)) != 0)
590 type
&= ~almtx_timed
;
591 if(type
== almtx_plain
)
592 ret
= pthread_mutex_init(mtx
, NULL
);
595 pthread_mutexattr_t attr
;
597 ret
= pthread_mutexattr_init(&attr
);
598 if(ret
) return althrd_error
;
600 if(type
== almtx_recursive
)
602 ret
= pthread_mutexattr_settype(&attr
, PTHREAD_MUTEX_RECURSIVE
);
603 #ifdef HAVE_PTHREAD_MUTEXATTR_SETKIND_NP
605 ret
= pthread_mutexattr_setkind_np(&attr
, PTHREAD_MUTEX_RECURSIVE
);
611 ret
= pthread_mutex_init(mtx
, &attr
);
612 pthread_mutexattr_destroy(&attr
);
614 return ret
? althrd_error
: althrd_success
;
617 void almtx_destroy(almtx_t
*mtx
)
619 pthread_mutex_destroy(mtx
);
622 int almtx_timedlock(almtx_t
*mtx
, const struct timespec
*ts
)
626 #ifdef HAVE_PTHREAD_MUTEX_TIMEDLOCK
627 ret
= pthread_mutex_timedlock(mtx
, ts
);
630 case 0: return althrd_success
;
631 case ETIMEDOUT
: return althrd_timedout
;
632 case EBUSY
: return althrd_busy
;
639 while((ret
=almtx_trylock(mtx
)) == althrd_busy
)
643 if(ts
->tv_sec
< 0 || ts
->tv_nsec
< 0 || ts
->tv_nsec
>= 1000000000 ||
644 altimespec_get(&now
, AL_TIME_UTC
) != AL_TIME_UTC
)
646 if(now
.tv_sec
> ts
->tv_sec
|| (now
.tv_sec
== ts
->tv_sec
&& now
.tv_nsec
>= ts
->tv_nsec
))
647 return althrd_timedout
;
656 int alcnd_init(alcnd_t
*cond
)
658 if(pthread_cond_init(cond
, NULL
) == 0)
659 return althrd_success
;
663 int alcnd_signal(alcnd_t
*cond
)
665 if(pthread_cond_signal(cond
) == 0)
666 return althrd_success
;
670 int alcnd_broadcast(alcnd_t
*cond
)
672 if(pthread_cond_broadcast(cond
) == 0)
673 return althrd_success
;
677 int alcnd_wait(alcnd_t
*cond
, almtx_t
*mtx
)
679 if(pthread_cond_wait(cond
, mtx
) == 0)
680 return althrd_success
;
684 int alcnd_timedwait(alcnd_t
*cond
, almtx_t
*mtx
, const struct timespec
*time_point
)
686 if(pthread_cond_timedwait(cond
, mtx
, time_point
) == 0)
687 return althrd_success
;
691 void alcnd_destroy(alcnd_t
*cond
)
693 pthread_cond_destroy(cond
);
697 int altss_create(altss_t
*tss_id
, altss_dtor_t callback
)
699 if(pthread_key_create(tss_id
, callback
) != 0)
701 return althrd_success
;
704 void altss_delete(altss_t tss_id
)
706 pthread_key_delete(tss_id
);
710 int altimespec_get(struct timespec
*ts
, int base
)
712 if(base
== AL_TIME_UTC
)
715 #if _POSIX_TIMERS > 0
716 ret
= clock_gettime(CLOCK_REALTIME
, ts
);
717 if(ret
== 0) return base
;
718 #else /* _POSIX_TIMERS > 0 */
720 ret
= gettimeofday(&tv
, NULL
);
723 ts
->tv_sec
= tv
.tv_sec
;
724 ts
->tv_nsec
= tv
.tv_usec
* 1000;
736 void al_nssleep(unsigned long nsec
)
738 struct timespec ts
, rem
;
739 ts
.tv_sec
= nsec
/ 1000000000ul;
740 ts
.tv_nsec
= nsec
% 1000000000ul;
742 while(althrd_sleep(&ts
, &rem
) == -1)