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
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 */
49 #define WIN32_LEAN_AND_MEAN
54 void althrd_setname(althrd_t thr
, const char *name
)
57 #define MS_VC_EXCEPTION 0x406D1388
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.
68 info
.dwThreadID
= thr
;
72 RaiseException(MS_VC_EXCEPTION
, 0, sizeof(info
)/sizeof(ULONG_PTR
), (ULONG_PTR
*)&info
);
74 __except(EXCEPTION_CONTINUE_EXECUTION
) {
76 #undef MS_VC_EXCEPTION
78 TRACE("Can't set thread %04lx name to \"%s\"\n", thr
, name
);
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
);
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
;
96 PIMAGE_TLS_CALLBACK althrd_callback_
= althrd_callback
;
100 typedef struct thread_cntr
{
105 static DWORD WINAPI
althrd_starter(void *arg
)
108 memcpy(&cntr
, arg
, sizeof(cntr
));
111 return (DWORD
)((*cntr
.func
)(cntr
.arg
));
115 int althrd_create(althrd_t
*thr
, althrd_start_t func
, void *arg
)
121 cntr
= malloc(sizeof(*cntr
));
122 if(!cntr
) return althrd_nomem
;
127 hdl
= CreateThread(NULL
, THREAD_STACK_SIZE
, althrd_starter
, cntr
, 0, &thrid
);
133 InsertUIntMapEntry(&ThrdIdHandle
, thrid
, hdl
);
136 return althrd_success
;
139 int althrd_detach(althrd_t thr
)
141 HANDLE hdl
= RemoveUIntMapKey(&ThrdIdHandle
, thr
);
142 if(!hdl
) return althrd_error
;
145 return althrd_success
;
148 int althrd_join(althrd_t thr
, int *res
)
152 HANDLE hdl
= RemoveUIntMapKey(&ThrdIdHandle
, thr
);
153 if(!hdl
) return althrd_error
;
155 WaitForSingleObject(hdl
, INFINITE
);
156 GetExitCodeThread(hdl
, &code
);
160 return althrd_success
;
163 int althrd_sleep(const struct timespec
*ts
, struct timespec
* UNUSED(rem
))
167 if(ts
->tv_sec
< 0 || ts
->tv_sec
>= (0x7fffffff / 1000) ||
168 ts
->tv_nsec
< 0 || ts
->tv_nsec
>= 1000000000)
171 msec
= (DWORD
)(ts
->tv_sec
* 1000);
172 msec
+= (DWORD
)((ts
->tv_nsec
+999999) / 1000000);
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
)
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
)
202 while((ret
=almtx_trylock(mtx
)) == althrd_busy
)
206 if(ts
->tv_sec
< 0 || ts
->tv_nsec
< 0 || ts
->tv_nsec
>= 1000000000 ||
207 altimespec_get(&now
, AL_TIME_UTC
) != AL_TIME_UTC
)
209 if(now
.tv_sec
> ts
->tv_sec
|| (now
.tv_sec
== ts
->tv_sec
&& now
.tv_nsec
>= ts
->tv_nsec
))
210 return althrd_timedout
;
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
))
232 if(reason
== DLL_PROCESS_DETACH
)
234 ResetUIntMap(&TlsDestructors
);
237 if(reason
!= DLL_THREAD_DETACH
)
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
;
248 UnlockUIntMapRead(&TlsDestructors
);
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
;
256 #warning "No TLS callback support, thread-local contexts may leak references on poorly written applications."
257 PIMAGE_TLS_CALLBACK altss_callback_
= altss_callback
;
260 int altss_create(altss_t
*tss_id
, altss_dtor_t callback
)
262 DWORD key
= TlsAlloc();
263 if(key
== TLS_OUT_OF_INDEXES
)
268 InsertUIntMapEntry(&TlsDestructors
, key
, callback
);
269 return althrd_success
;
272 void altss_delete(altss_t tss_id
)
274 RemoveUIntMapKey(&TlsDestructors
, tss_id
);
279 int altimespec_get(struct timespec
*ts
, int base
)
281 if(base
== AL_TIME_UTC
)
285 ULARGE_INTEGER ulint
;
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;
298 void alcall_once(alonce_flag
*once
, void (*callback
)(void))
301 while((ret
=InterlockedExchange(once
, 1)) == 1)
305 InterlockedExchange(once
, 2);
310 #include <sys/time.h>
313 #ifdef HAVE_PTHREAD_NP_H
314 #include <pthread_np.h>
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)
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
);
336 TRACE("Can't set thread name to \"%s\"\n", name
);
341 typedef struct thread_cntr
{
346 static void *althrd_starter(void *arg
)
349 memcpy(&cntr
, arg
, sizeof(cntr
));
352 return (void*)(intptr_t)((*cntr
.func
)(cntr
.arg
));
356 int althrd_create(althrd_t
*thr
, althrd_start_t func
, void *arg
)
361 cntr
= malloc(sizeof(*cntr
));
362 if(!cntr
) return althrd_nomem
;
364 if(pthread_attr_init(&attr
) != 0)
369 if(pthread_attr_setstacksize(&attr
, THREAD_STACK_SIZE
) != 0)
371 pthread_attr_destroy(&attr
);
378 if(pthread_create(thr
, &attr
, althrd_starter
, cntr
) != 0)
380 pthread_attr_destroy(&attr
);
384 pthread_attr_destroy(&attr
);
386 return althrd_success
;
389 int althrd_detach(althrd_t thr
)
391 if(pthread_detach(thr
) != 0)
393 return althrd_success
;
396 int althrd_join(althrd_t thr
, int *res
)
400 if(!res
) return althrd_error
;
402 if(pthread_join(thr
, &code
) != 0)
404 *res
= (int)(intptr_t)code
;
405 return althrd_success
;
409 int almtx_init(almtx_t
*mtx
, int type
)
413 if(!mtx
) return althrd_error
;
414 if((type
&~(almtx_recursive
|almtx_timed
)) != 0)
417 type
&= ~almtx_timed
;
418 if(type
== almtx_plain
)
419 ret
= pthread_mutex_init(mtx
, NULL
);
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
432 ret
= pthread_mutexattr_setkind_np(&attr
, PTHREAD_MUTEX_RECURSIVE
);
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
)
453 #ifdef HAVE_PTHREAD_MUTEX_TIMEDLOCK
454 ret
= pthread_mutex_timedlock(mtx
, ts
);
457 case 0: return althrd_success
;
458 case ETIMEDOUT
: return althrd_timedout
;
459 case EBUSY
: return althrd_busy
;
466 while((ret
=almtx_trylock(mtx
)) == althrd_busy
)
470 if(ts
->tv_sec
< 0 || ts
->tv_nsec
< 0 || ts
->tv_nsec
>= 1000000000 ||
471 altimespec_get(&now
, AL_TIME_UTC
) != AL_TIME_UTC
)
473 if(now
.tv_sec
> ts
->tv_sec
|| (now
.tv_sec
== ts
->tv_sec
&& now
.tv_nsec
>= ts
->tv_nsec
))
474 return althrd_timedout
;
484 int altss_create(altss_t
*tss_id
, altss_dtor_t callback
)
486 if(pthread_key_create(tss_id
, callback
) != 0)
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
)
502 #if _POSIX_TIMERS > 0
503 ret
= clock_gettime(CLOCK_REALTIME
, ts
);
504 if(ret
== 0) return base
;
505 #else /* _POSIX_TIMERS > 0 */
507 ret
= gettimeofday(&tv
, NULL
);
510 ts
->tv_sec
= tv
.tv_sec
;
511 ts
->tv_nsec
= tv
.tv_usec
* 1000;
523 void al_nssleep(time_t sec
, long nsec
)
525 struct timespec ts
, rem
;
529 while(althrd_sleep(&ts
, &rem
) == -1)