2 Simple DirectMedia Layer
3 Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
21 #include "../SDL_internal.h"
23 /* System independent thread management routines for SDL */
25 #include "SDL_assert.h"
26 #include "SDL_thread.h"
27 #include "SDL_thread_c.h"
28 #include "SDL_systhread.h"
29 #include "../SDL_error_c.h"
35 static SDL_atomic_t SDL_tls_id;
36 return SDL_AtomicIncRef(&SDL_tls_id)+1;
40 SDL_TLSGet(SDL_TLSID id)
44 storage = SDL_SYS_GetTLSData();
45 if (!storage || id == 0 || id > storage->limit) {
48 return storage->array[id-1].data;
52 SDL_TLSSet(SDL_TLSID id, const void *value, void (*destructor)(void *))
57 return SDL_InvalidParamError("id");
60 storage = SDL_SYS_GetTLSData();
61 if (!storage || (id > storage->limit)) {
62 unsigned int i, oldlimit, newlimit;
64 oldlimit = storage ? storage->limit : 0;
65 newlimit = (id + TLS_ALLOC_CHUNKSIZE);
66 storage = (SDL_TLSData *)SDL_realloc(storage, sizeof(*storage)+(newlimit-1)*sizeof(storage->array[0]));
68 return SDL_OutOfMemory();
70 storage->limit = newlimit;
71 for (i = oldlimit; i < newlimit; ++i) {
72 storage->array[i].data = NULL;
73 storage->array[i].destructor = NULL;
75 if (SDL_SYS_SetTLSData(storage) != 0) {
80 storage->array[id-1].data = SDL_const_cast(void*, value);
81 storage->array[id-1].destructor = destructor;
90 storage = SDL_SYS_GetTLSData();
93 for (i = 0; i < storage->limit; ++i) {
94 if (storage->array[i].destructor) {
95 storage->array[i].destructor(storage->array[i].data);
98 SDL_SYS_SetTLSData(NULL);
104 /* This is a generic implementation of thread-local storage which doesn't
105 require additional OS support.
107 It is not especially efficient and doesn't clean up thread-local storage
108 as threads exit. If there is a real OS that doesn't support thread-local
109 storage this implementation should be improved to be production quality.
112 typedef struct SDL_TLSEntry {
114 SDL_TLSData *storage;
115 struct SDL_TLSEntry *next;
118 static SDL_mutex *SDL_generic_TLS_mutex;
119 static SDL_TLSEntry *SDL_generic_TLS;
123 SDL_Generic_GetTLSData()
125 SDL_threadID thread = SDL_ThreadID();
127 SDL_TLSData *storage = NULL;
129 #if !SDL_THREADS_DISABLED
130 if (!SDL_generic_TLS_mutex) {
131 static SDL_SpinLock tls_lock;
132 SDL_AtomicLock(&tls_lock);
133 if (!SDL_generic_TLS_mutex) {
134 SDL_mutex *mutex = SDL_CreateMutex();
135 SDL_MemoryBarrierRelease();
136 SDL_generic_TLS_mutex = mutex;
137 if (!SDL_generic_TLS_mutex) {
138 SDL_AtomicUnlock(&tls_lock);
142 SDL_AtomicUnlock(&tls_lock);
144 #endif /* SDL_THREADS_DISABLED */
146 SDL_MemoryBarrierAcquire();
147 SDL_LockMutex(SDL_generic_TLS_mutex);
148 for (entry = SDL_generic_TLS; entry; entry = entry->next) {
149 if (entry->thread == thread) {
150 storage = entry->storage;
154 #if !SDL_THREADS_DISABLED
155 SDL_UnlockMutex(SDL_generic_TLS_mutex);
162 SDL_Generic_SetTLSData(SDL_TLSData *storage)
164 SDL_threadID thread = SDL_ThreadID();
165 SDL_TLSEntry *prev, *entry;
167 /* SDL_Generic_GetTLSData() is always called first, so we can assume SDL_generic_TLS_mutex */
168 SDL_LockMutex(SDL_generic_TLS_mutex);
170 for (entry = SDL_generic_TLS; entry; entry = entry->next) {
171 if (entry->thread == thread) {
173 entry->storage = storage;
176 prev->next = entry->next;
178 SDL_generic_TLS = entry->next;
187 entry = (SDL_TLSEntry *)SDL_malloc(sizeof(*entry));
189 entry->thread = thread;
190 entry->storage = storage;
191 entry->next = SDL_generic_TLS;
192 SDL_generic_TLS = entry;
195 SDL_UnlockMutex(SDL_generic_TLS_mutex);
198 return SDL_OutOfMemory();
203 /* Routine to get the thread-specific error variable */
207 static SDL_SpinLock tls_lock;
208 static SDL_bool tls_being_created;
209 static SDL_TLSID tls_errbuf;
210 static SDL_error SDL_global_errbuf;
211 const SDL_error *ALLOCATION_IN_PROGRESS = (SDL_error *)-1;
214 /* tls_being_created is there simply to prevent recursion if SDL_TLSCreate() fails.
215 It also means it's possible for another thread to also use SDL_global_errbuf,
216 but that's very unlikely and hopefully won't cause issues.
218 if (!tls_errbuf && !tls_being_created) {
219 SDL_AtomicLock(&tls_lock);
222 tls_being_created = SDL_TRUE;
223 slot = SDL_TLSCreate();
224 tls_being_created = SDL_FALSE;
225 SDL_MemoryBarrierRelease();
228 SDL_AtomicUnlock(&tls_lock);
231 return &SDL_global_errbuf;
234 SDL_MemoryBarrierAcquire();
235 errbuf = (SDL_error *)SDL_TLSGet(tls_errbuf);
236 if (errbuf == ALLOCATION_IN_PROGRESS) {
237 return &SDL_global_errbuf;
240 /* Mark that we're in the middle of allocating our buffer */
241 SDL_TLSSet(tls_errbuf, ALLOCATION_IN_PROGRESS, NULL);
242 errbuf = (SDL_error *)SDL_malloc(sizeof(*errbuf));
244 SDL_TLSSet(tls_errbuf, NULL, NULL);
245 return &SDL_global_errbuf;
248 SDL_TLSSet(tls_errbuf, errbuf, SDL_free);
254 /* Arguments and callback to setup and run the user thread function */
257 int (SDLCALL * func) (void *);
264 SDL_RunThread(void *data)
266 thread_args *args = (thread_args *) data;
267 int (SDLCALL * userfunc) (void *) = args->func;
268 void *userdata = args->data;
269 SDL_Thread *thread = args->info;
270 int *statusloc = &thread->status;
272 /* Perform any system-dependent setup - this function may not fail */
273 SDL_SYS_SetupThread(thread->name);
275 /* Get the thread id */
276 thread->threadid = SDL_ThreadID();
278 /* Wake up the parent thread */
279 SDL_SemPost(args->wait);
281 /* Run the function */
282 *statusloc = userfunc(userdata);
284 /* Clean up thread-local storage */
287 /* Mark us as ready to be joined (or detached) */
288 if (!SDL_AtomicCAS(&thread->state, SDL_THREAD_STATE_ALIVE, SDL_THREAD_STATE_ZOMBIE)) {
289 /* Clean up if something already detached us. */
290 if (SDL_AtomicCAS(&thread->state, SDL_THREAD_STATE_DETACHED, SDL_THREAD_STATE_CLEANED)) {
292 SDL_free(thread->name);
299 #ifdef SDL_CreateThread
300 #undef SDL_CreateThread
303 #define SDL_CreateThread SDL_CreateThread_REAL
306 #ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
307 DECLSPEC SDL_Thread *SDLCALL
308 SDL_CreateThread(int (SDLCALL * fn) (void *),
309 const char *name, void *data,
310 pfnSDL_CurrentBeginThread pfnBeginThread,
311 pfnSDL_CurrentEndThread pfnEndThread)
313 DECLSPEC SDL_Thread *SDLCALL
314 SDL_CreateThread(int (SDLCALL * fn) (void *),
315 const char *name, void *data)
322 /* Allocate memory for the thread info structure */
323 thread = (SDL_Thread *) SDL_malloc(sizeof(*thread));
324 if (thread == NULL) {
330 SDL_AtomicSet(&thread->state, SDL_THREAD_STATE_ALIVE);
332 /* Set up the arguments for the thread */
334 thread->name = SDL_strdup(name);
335 if (thread->name == NULL) {
342 /* Set up the arguments for the thread */
343 args = (thread_args *) SDL_malloc(sizeof(*args));
347 SDL_free(thread->name);
355 args->wait = SDL_CreateSemaphore(0);
356 if (args->wait == NULL) {
358 SDL_free(thread->name);
365 /* Create the thread and go! */
366 #ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
367 ret = SDL_SYS_CreateThread(thread, args, pfnBeginThread, pfnEndThread);
369 ret = SDL_SYS_CreateThread(thread, args);
372 /* Wait for the thread function to use arguments */
373 SDL_SemWait(args->wait);
375 /* Oops, failed. Gotta free everything */
377 SDL_free(thread->name);
382 SDL_DestroySemaphore(args->wait);
385 /* Everything is running now */
390 SDL_GetThreadID(SDL_Thread * thread)
395 id = thread->threadid;
403 SDL_GetThreadName(SDL_Thread * thread)
413 SDL_SetThreadPriority(SDL_ThreadPriority priority)
415 return SDL_SYS_SetThreadPriority(priority);
419 SDL_WaitThread(SDL_Thread * thread, int *status)
422 SDL_SYS_WaitThread(thread);
424 *status = thread->status;
427 SDL_free(thread->name);
434 SDL_DetachThread(SDL_Thread * thread)
440 /* Grab dibs if the state is alive+joinable. */
441 if (SDL_AtomicCAS(&thread->state, SDL_THREAD_STATE_ALIVE, SDL_THREAD_STATE_DETACHED)) {
442 SDL_SYS_DetachThread(thread);
444 /* all other states are pretty final, see where we landed. */
445 const int thread_state = SDL_AtomicGet(&thread->state);
446 if ((thread_state == SDL_THREAD_STATE_DETACHED) || (thread_state == SDL_THREAD_STATE_CLEANED)) {
447 return; /* already detached (you shouldn't call this twice!) */
448 } else if (thread_state == SDL_THREAD_STATE_ZOMBIE) {
449 SDL_WaitThread(thread, NULL); /* already done, clean it up. */
451 SDL_assert(0 && "Unexpected thread state");
456 /* vi: set ts=4 sw=4 expandtab: */