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 /* 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
)
86 #define MS_VC_EXCEPTION 0x406D1388
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.
97 info
.dwThreadID
= thr
;
101 RaiseException(MS_VC_EXCEPTION
, 0, sizeof(info
)/sizeof(ULONG_PTR
), (ULONG_PTR
*)&info
);
103 __except(EXCEPTION_CONTINUE_EXECUTION
) {
105 #undef MS_VC_EXCEPTION
113 typedef struct thread_cntr
{
118 static DWORD WINAPI
althrd_starter(void *arg
)
121 memcpy(&cntr
, arg
, sizeof(cntr
));
124 return (DWORD
)((*cntr
.func
)(cntr
.arg
));
128 int althrd_create(althrd_t
*thr
, althrd_start_t func
, void *arg
)
134 cntr
= malloc(sizeof(*cntr
));
135 if(!cntr
) return althrd_nomem
;
140 hdl
= CreateThread(NULL
, THREAD_STACK_SIZE
, althrd_starter
, cntr
, 0, &thrid
);
146 InsertUIntMapEntry(&ThrdIdHandle
, thrid
, hdl
);
149 return althrd_success
;
152 int althrd_detach(althrd_t thr
)
154 HANDLE hdl
= RemoveUIntMapKey(&ThrdIdHandle
, thr
);
155 if(!hdl
) return althrd_error
;
158 return althrd_success
;
161 int althrd_join(althrd_t thr
, int *res
)
165 HANDLE hdl
= RemoveUIntMapKey(&ThrdIdHandle
, thr
);
166 if(!hdl
) return althrd_error
;
168 WaitForSingleObject(hdl
, INFINITE
);
169 GetExitCodeThread(hdl
, &code
);
174 return althrd_success
;
177 int althrd_sleep(const struct timespec
*ts
, struct timespec
* UNUSED(rem
))
181 if(ts
->tv_sec
< 0 || ts
->tv_sec
>= (0x7fffffff / 1000) ||
182 ts
->tv_nsec
< 0 || ts
->tv_nsec
>= 1000000000)
185 msec
= (DWORD
)(ts
->tv_sec
* 1000);
186 msec
+= (DWORD
)((ts
->tv_nsec
+999999) / 1000000);
193 int almtx_init(almtx_t
*mtx
, int type
)
195 if(!mtx
) return althrd_error
;
197 type
&= ~almtx_recursive
;
198 if(type
!= almtx_plain
)
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
;
236 void alcnd_destroy(alcnd_t
* UNUSED(cond
))
238 /* Nothing to delete? */
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.
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
]);
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
;
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
]);
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
;
337 void alsem_destroy(alsem_t
*sem
)
342 int alsem_post(alsem_t
*sem
)
344 DWORD ret
= ReleaseSemaphore(*sem
, 1, NULL
);
345 if(ret
) return althrd_success
;
349 int alsem_wait(alsem_t
*sem
)
351 DWORD ret
= WaitForSingleObject(*sem
, INFINITE
);
352 if(ret
== WAIT_OBJECT_0
) return althrd_success
;
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
;
365 int altss_create(altss_t
*tss_id
, altss_dtor_t callback
)
367 DWORD key
= TlsAlloc();
368 if(key
== TLS_OUT_OF_INDEXES
)
373 InsertUIntMapEntry(&TlsDestructors
, key
, callback
);
374 return althrd_success
;
377 void altss_delete(altss_t tss_id
)
379 RemoveUIntMapKey(&TlsDestructors
, 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
)
392 ULARGE_INTEGER ulint
;
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;
405 void alcall_once(alonce_flag
*once
, void (*callback
)(void))
408 while((ret
=InterlockedExchange(once
, 1)) == 1)
412 InterlockedExchange(once
, 2);
416 void althrd_deinit(void)
418 ResetUIntMap(&ThrdIdHandle
);
419 ResetUIntMap(&TlsDestructors
);
422 void althrd_thread_detach(void)
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
];
433 if(callback
) callback(ptr
);
434 altss_set(TlsDestructors
.keys
[i
], NULL
);
437 UnlockUIntMapRead(&TlsDestructors
);
442 #include <sys/time.h>
445 #ifdef HAVE_PTHREAD_NP_H
446 #include <pthread_np.h>
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
);
465 pthread_setname_np(thr
, name
);
467 #elif defined(HAVE_PTHREAD_SET_NAME_NP)
468 pthread_set_name_np(thr
, name
);
476 typedef struct thread_cntr
{
481 static void *althrd_starter(void *arg
)
484 memcpy(&cntr
, arg
, sizeof(cntr
));
487 return (void*)(intptr_t)((*cntr
.func
)(cntr
.arg
));
491 int althrd_create(althrd_t
*thr
, althrd_start_t func
, void *arg
)
495 size_t stackmult
= 1;
498 cntr
= malloc(sizeof(*cntr
));
499 if(!cntr
) return althrd_nomem
;
501 if(pthread_attr_init(&attr
) != 0)
507 if(pthread_attr_setstacksize(&attr
, THREAD_STACK_SIZE
*stackmult
) != 0)
509 pthread_attr_destroy(&attr
);
516 if((err
=pthread_create(thr
, &attr
, althrd_starter
, cntr
)) == 0)
518 pthread_attr_destroy(&attr
);
519 return althrd_success
;
524 /* If an invalid stack size, try increasing it (limit x4, 8MB). */
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
);
542 int althrd_detach(althrd_t thr
)
544 if(pthread_detach(thr
) != 0)
546 return althrd_success
;
549 int althrd_join(althrd_t thr
, int *res
)
553 if(pthread_join(thr
, &code
) != 0)
556 *res
= (int)(intptr_t)code
;
557 return althrd_success
;
561 int almtx_init(almtx_t
*mtx
, int type
)
565 if(!mtx
) return althrd_error
;
566 if((type
&~almtx_recursive
) != 0)
569 if(type
== almtx_plain
)
570 ret
= pthread_mutex_init(mtx
, NULL
);
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
583 ret
= pthread_mutexattr_setkind_np(&attr
, PTHREAD_MUTEX_RECURSIVE
);
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
;
607 int alcnd_signal(alcnd_t
*cond
)
609 if(pthread_cond_signal(cond
) == 0)
610 return althrd_success
;
614 int alcnd_broadcast(alcnd_t
*cond
)
616 if(pthread_cond_broadcast(cond
) == 0)
617 return althrd_success
;
621 int alcnd_wait(alcnd_t
*cond
, almtx_t
*mtx
)
623 if(pthread_cond_wait(cond
, mtx
) == 0)
624 return althrd_success
;
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
;
641 void alsem_destroy(alsem_t
*sem
)
646 int alsem_post(alsem_t
*sem
)
648 if(sem_post(sem
) == 0)
649 return althrd_success
;
653 int alsem_wait(alsem_t
*sem
)
655 if(sem_wait(sem
) == 0) return althrd_success
;
656 if(errno
== EINTR
) return -2;
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;
669 int altss_create(altss_t
*tss_id
, altss_dtor_t callback
)
671 if(pthread_key_create(tss_id
, callback
) != 0)
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
)
687 #if _POSIX_TIMERS > 0
688 ret
= clock_gettime(CLOCK_REALTIME
, ts
);
689 if(ret
== 0) return base
;
690 #else /* _POSIX_TIMERS > 0 */
692 ret
= gettimeofday(&tv
, NULL
);
695 ts
->tv_sec
= tv
.tv_sec
;
696 ts
->tv_nsec
= tv
.tv_usec
* 1000;
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)