Keep TlsDestructors within threads.c
[openal-soft.git] / Alc / threads.c
blobcf9e5bed02a771fd0900955c3d098d31641a33c2
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 int althrd_equal(althrd_t thr0, althrd_t thr1);
33 extern inline void althrd_exit(int res);
34 extern inline void althrd_yield(void);
36 extern inline int almtx_lock(almtx_t *mtx);
37 extern inline int almtx_unlock(almtx_t *mtx);
38 extern inline int almtx_trylock(almtx_t *mtx);
40 extern inline void *altss_get(altss_t tss_id);
41 extern inline int altss_set(altss_t tss_id, void *val);
43 extern inline void al_nssleep(time_t sec, long nsec);
46 #define THREAD_STACK_SIZE (1*1024*1024) /* 1MB */
48 #ifdef _WIN32
50 #define WIN32_LEAN_AND_MEAN
51 #include <windows.h>
52 #include <mmsystem.h>
55 void SetThreadName(const char *name)
57 #if defined(_MSC_VER)
58 #define MS_VC_EXCEPTION 0x406D1388
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 info.dwType = 0x1000;
66 info.szName = name;
67 info.dwThreadID = -1;
68 info.dwFlags = 0;
70 __try {
71 RaiseException(MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(DWORD), (ULONG_PTR*)&info);
73 __except(EXCEPTION_CONTINUE_EXECUTION) {
75 #undef MS_VC_EXCEPTION
76 #else
77 TRACE("Can't set thread %04lx name to \"%s\"\n", GetCurrentThreadId(), name);
78 #endif
82 typedef struct thread_cntr {
83 althrd_start_t func;
84 void *arg;
85 } thread_cntr;
87 static DWORD WINAPI althrd_starter(void *arg)
89 thread_cntr cntr;
90 memcpy(&cntr, arg, sizeof(cntr));
91 free(arg);
93 return (DWORD)((*cntr.func)(cntr.arg));
97 int althrd_create(althrd_t *thr, althrd_start_t func, void *arg)
99 thread_cntr *cntr;
100 DWORD dummy;
101 HANDLE hdl;
103 cntr = malloc(sizeof(*cntr));
104 if(!cntr) return althrd_nomem;
106 cntr->func = func;
107 cntr->arg = arg;
109 hdl = CreateThread(NULL, THREAD_STACK_SIZE, althrd_starter, cntr, 0, &dummy);
110 if(!hdl)
112 free(cntr);
113 return althrd_error;
116 *thr = hdl;
117 return althrd_success;
120 int althrd_detach(althrd_t thr)
122 if(!thr) return althrd_error;
123 CloseHandle(thr);
125 return althrd_success;
128 int althrd_join(althrd_t thr, int *res)
130 DWORD code;
132 if(!thr) return althrd_error;
134 WaitForSingleObject(thr, INFINITE);
135 GetExitCodeThread(thr, &code);
136 CloseHandle(thr);
138 *res = (int)code;
139 return althrd_success;
142 int althrd_sleep(const struct timespec *ts, struct timespec* UNUSED(rem))
144 DWORD msec;
146 if(ts->tv_sec < 0 || ts->tv_sec >= (0xffffffffu / 1000) ||
147 ts->tv_nsec < 0 || ts->tv_nsec >= 1000000000)
148 return -2;
150 msec = (DWORD)(ts->tv_sec * 1000);
151 msec += (DWORD)((ts->tv_nsec+999999) / 1000000);
152 Sleep(msec);
154 return 0;
158 int almtx_init(almtx_t *mtx, int type)
160 if(!mtx) return althrd_error;
161 type &= ~(almtx_recursive|almtx_timed);
162 if(type != almtx_plain)
163 return althrd_error;
165 InitializeCriticalSection(mtx);
166 return althrd_success;
169 void almtx_destroy(almtx_t *mtx)
171 DeleteCriticalSection(mtx);
174 int almtx_timedlock(almtx_t *mtx, const struct timespec *ts)
176 DWORD start, timelen;
177 int ret;
179 if(!mtx || !ts)
180 return althrd_error;
182 timelen = ts->tv_sec * 1000;
183 timelen += (ts->tv_nsec+999999) / 1000000;
185 start = timeGetTime();
186 while((ret=almtx_trylock(mtx)) == althrd_busy)
188 DWORD now = timeGetTime();
189 if(now-start >= timelen)
191 ret = althrd_timedout;
192 break;
194 SwitchToThread();
197 return ret;
201 /* An associative map of uint:void* pairs. The key is the TLS index (given by
202 * TlsAlloc), and the value is the altss_dtor_t callback. When a thread exits,
203 * it iterates over the thread-local value for each TLS key and calls the
204 * destructor function if they're both not NULL. Placing a PIMAGE_TLS_CALLBACK
205 * function pointer in a ".CRT$XLx" section (where x is a character A to Z)
206 * ensures the CRT will call it similar to DllMain.
208 static UIntMap TlsDestructors = UINTMAP_STATIC_INITIALIZE;
210 static void NTAPI altss_callback(void* UNUSED(handle), DWORD reason, void* UNUSED(reserved))
212 ALsizei i;
214 if(reason == DLL_PROCESS_DETACH)
216 ResetUIntMap(&TlsDestructors);
217 return;
219 if(reason != DLL_THREAD_DETACH)
220 return;
222 LockUIntMapRead(&TlsDestructors);
223 for(i = 0;i < TlsDestructors.size;i++)
225 void *ptr = altss_get(TlsDestructors.array[i].key);
226 altss_dtor_t callback = (altss_dtor_t)TlsDestructors.array[i].value;
227 if(ptr && callback)
228 callback(ptr);
230 UnlockUIntMapRead(&TlsDestructors);
232 #ifdef _MSC_VER
233 #pragma section(".CRT$XLB",read)
234 __declspec(allocate(".CRT$XLB")) PIMAGE_TLS_CALLBACK altss_callback_ = altss_callback;
235 #elif defined(__GNUC__)
236 PIMAGE_TLS_CALLBACK altss_callback_ __attribute__((section(".CRT$XLB"))) = altss_callback;
237 #else
238 #warning "No TLS callback support, thread-local contexts may leak references on poorly written applications."
239 PIMAGE_TLS_CALLBACK altss_callback_ = altss_callback;
240 #endif
242 int altss_create(altss_t *tss_id, altss_dtor_t callback)
244 DWORD key = TlsAlloc();
245 if(key == TLS_OUT_OF_INDEXES)
246 return althrd_error;
248 *tss_id = key;
249 if(callback != NULL)
250 InsertUIntMapEntry(&TlsDestructors, key, callback);
251 return althrd_success;
254 void altss_delete(altss_t tss_id)
256 RemoveUIntMapKey(&TlsDestructors, tss_id);
257 TlsFree(tss_id);
260 #else
262 #include <pthread.h>
263 #ifdef HAVE_PTHREAD_NP_H
264 #include <pthread_np.h>
265 #endif
268 extern inline althrd_t althrd_current(void);
269 extern inline int althrd_sleep(const struct timespec *ts, struct timespec *rem);
272 void SetThreadName(const char *name)
274 #if defined(HAVE_PTHREAD_SETNAME_NP)
275 #if defined(__GNUC__)
276 if(pthread_setname_np(pthread_self(), name) != 0)
277 #elif defined(__APPLE__)
278 if(pthread_setname_np(name) != 0)
279 #endif
280 WARN("Failed to set thread name to \"%s\": %s\n", name, strerror(errno));
281 #elif defined(HAVE_PTHREAD_SET_NAME_NP)
282 pthread_set_name_np(pthread_self(), name);
283 #else
284 TRACE("Can't set thread name to \"%s\"\n", name);
285 #endif
289 typedef struct thread_cntr {
290 althrd_start_t func;
291 void *arg;
292 } thread_cntr;
294 static void *althrd_starter(void *arg)
296 thread_cntr cntr;
297 memcpy(&cntr, arg, sizeof(cntr));
298 free(arg);
300 return (void*)(intptr_t)((*cntr.func)(cntr.arg));
304 int althrd_create(althrd_t *thr, althrd_start_t func, void *arg)
306 thread_cntr *cntr;
307 pthread_attr_t attr;
309 cntr = malloc(sizeof(*cntr));
310 if(!cntr) return althrd_nomem;
312 if(pthread_attr_init(&attr) != 0)
314 free(cntr);
315 return althrd_error;
317 if(pthread_attr_setstacksize(&attr, THREAD_STACK_SIZE) != 0)
319 pthread_attr_destroy(&attr);
320 free(cntr);
321 return althrd_error;
324 cntr->func = func;
325 cntr->arg = arg;
326 if(pthread_create(thr, &attr, althrd_starter, cntr) != 0)
328 pthread_attr_destroy(&attr);
329 free(cntr);
330 return althrd_error;
332 pthread_attr_destroy(&attr);
334 return althrd_success;
337 int althrd_detach(althrd_t thr)
339 if(pthread_detach(thr) != 0)
340 return althrd_error;
341 return althrd_success;
344 int althrd_join(althrd_t thr, int *res)
346 void *code;
348 if(!res) return althrd_error;
350 if(pthread_join(thr, &code) != 0)
351 return althrd_error;
352 *res = (int)(intptr_t)code;
353 return althrd_success;
357 int almtx_init(almtx_t *mtx, int type)
359 int ret;
361 if(!mtx) return althrd_error;
362 if((type&~(almtx_recursive|almtx_timed)) != 0)
363 return althrd_error;
365 type &= ~almtx_timed;
366 if(type == almtx_plain)
367 ret = pthread_mutex_init(mtx, NULL);
368 else
370 pthread_mutexattr_t attr;
372 ret = pthread_mutexattr_init(&attr);
373 if(ret) return althrd_error;
375 if(type == almtx_recursive)
377 ret = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
378 #ifdef HAVE_PTHREAD_NP_H
379 if(ret != 0)
380 ret = pthread_mutexattr_setkind_np(&attr, PTHREAD_MUTEX_RECURSIVE);
381 #endif
383 else
384 ret = 1;
385 if(ret == 0)
386 ret = pthread_mutex_init(mtx, &attr);
387 pthread_mutexattr_destroy(&attr);
389 return ret ? althrd_error : althrd_success;
392 void almtx_destroy(almtx_t *mtx)
394 pthread_mutex_destroy(mtx);
397 int almtx_timedlock(almtx_t *mtx, const struct timespec *ts)
399 int ret;
401 if(!mtx || !ts)
402 return althrd_error;
404 ret = pthread_mutex_timedlock(mtx, ts);
405 switch(ret)
407 case 0: return althrd_success;
408 case ETIMEDOUT: return althrd_timedout;
409 case EAGAIN:
410 case EBUSY: return althrd_busy;
412 return althrd_error;
416 int altss_create(altss_t *tss_id, altss_dtor_t callback)
418 if(pthread_key_create(tss_id, callback) != 0)
419 return althrd_error;
420 return althrd_success;
423 void altss_delete(altss_t tss_id)
425 pthread_key_delete(tss_id);
428 #endif