SDL_thread.c (12401B)
1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org> 4 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. 8 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: 12 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. 20*/ 21#include "../SDL_internal.h" 22 23/* System independent thread management routines for SDL */ 24 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" 30 31 32SDL_TLSID 33SDL_TLSCreate() 34{ 35 static SDL_atomic_t SDL_tls_id; 36 return SDL_AtomicIncRef(&SDL_tls_id)+1; 37} 38 39void * 40SDL_TLSGet(SDL_TLSID id) 41{ 42 SDL_TLSData *storage; 43 44 storage = SDL_SYS_GetTLSData(); 45 if (!storage || id == 0 || id > storage->limit) { 46 return NULL; 47 } 48 return storage->array[id-1].data; 49} 50 51int 52SDL_TLSSet(SDL_TLSID id, const void *value, void (*destructor)(void *)) 53{ 54 SDL_TLSData *storage; 55 56 if (id == 0) { 57 return SDL_InvalidParamError("id"); 58 } 59 60 storage = SDL_SYS_GetTLSData(); 61 if (!storage || (id > storage->limit)) { 62 unsigned int i, oldlimit, newlimit; 63 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])); 67 if (!storage) { 68 return SDL_OutOfMemory(); 69 } 70 storage->limit = newlimit; 71 for (i = oldlimit; i < newlimit; ++i) { 72 storage->array[i].data = NULL; 73 storage->array[i].destructor = NULL; 74 } 75 if (SDL_SYS_SetTLSData(storage) != 0) { 76 return -1; 77 } 78 } 79 80 storage->array[id-1].data = SDL_const_cast(void*, value); 81 storage->array[id-1].destructor = destructor; 82 return 0; 83} 84 85static void 86SDL_TLSCleanup() 87{ 88 SDL_TLSData *storage; 89 90 storage = SDL_SYS_GetTLSData(); 91 if (storage) { 92 unsigned int i; 93 for (i = 0; i < storage->limit; ++i) { 94 if (storage->array[i].destructor) { 95 storage->array[i].destructor(storage->array[i].data); 96 } 97 } 98 SDL_SYS_SetTLSData(NULL); 99 SDL_free(storage); 100 } 101} 102 103 104/* This is a generic implementation of thread-local storage which doesn't 105 require additional OS support. 106 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. 110*/ 111 112typedef struct SDL_TLSEntry { 113 SDL_threadID thread; 114 SDL_TLSData *storage; 115 struct SDL_TLSEntry *next; 116} SDL_TLSEntry; 117 118static SDL_mutex *SDL_generic_TLS_mutex; 119static SDL_TLSEntry *SDL_generic_TLS; 120 121 122SDL_TLSData * 123SDL_Generic_GetTLSData() 124{ 125 SDL_threadID thread = SDL_ThreadID(); 126 SDL_TLSEntry *entry; 127 SDL_TLSData *storage = NULL; 128 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); 139 return NULL; 140 } 141 } 142 SDL_AtomicUnlock(&tls_lock); 143 } 144#endif /* SDL_THREADS_DISABLED */ 145 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; 151 break; 152 } 153 } 154#if !SDL_THREADS_DISABLED 155 SDL_UnlockMutex(SDL_generic_TLS_mutex); 156#endif 157 158 return storage; 159} 160 161int 162SDL_Generic_SetTLSData(SDL_TLSData *storage) 163{ 164 SDL_threadID thread = SDL_ThreadID(); 165 SDL_TLSEntry *prev, *entry; 166 167 /* SDL_Generic_GetTLSData() is always called first, so we can assume SDL_generic_TLS_mutex */ 168 SDL_LockMutex(SDL_generic_TLS_mutex); 169 prev = NULL; 170 for (entry = SDL_generic_TLS; entry; entry = entry->next) { 171 if (entry->thread == thread) { 172 if (storage) { 173 entry->storage = storage; 174 } else { 175 if (prev) { 176 prev->next = entry->next; 177 } else { 178 SDL_generic_TLS = entry->next; 179 } 180 SDL_free(entry); 181 } 182 break; 183 } 184 prev = entry; 185 } 186 if (!entry) { 187 entry = (SDL_TLSEntry *)SDL_malloc(sizeof(*entry)); 188 if (entry) { 189 entry->thread = thread; 190 entry->storage = storage; 191 entry->next = SDL_generic_TLS; 192 SDL_generic_TLS = entry; 193 } 194 } 195 SDL_UnlockMutex(SDL_generic_TLS_mutex); 196 197 if (!entry) { 198 return SDL_OutOfMemory(); 199 } 200 return 0; 201} 202 203/* Routine to get the thread-specific error variable */ 204SDL_error * 205SDL_GetErrBuf(void) 206{ 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; 212 SDL_error *errbuf; 213 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. 217 */ 218 if (!tls_errbuf && !tls_being_created) { 219 SDL_AtomicLock(&tls_lock); 220 if (!tls_errbuf) { 221 SDL_TLSID slot; 222 tls_being_created = SDL_TRUE; 223 slot = SDL_TLSCreate(); 224 tls_being_created = SDL_FALSE; 225 SDL_MemoryBarrierRelease(); 226 tls_errbuf = slot; 227 } 228 SDL_AtomicUnlock(&tls_lock); 229 } 230 if (!tls_errbuf) { 231 return &SDL_global_errbuf; 232 } 233 234 SDL_MemoryBarrierAcquire(); 235 errbuf = (SDL_error *)SDL_TLSGet(tls_errbuf); 236 if (errbuf == ALLOCATION_IN_PROGRESS) { 237 return &SDL_global_errbuf; 238 } 239 if (!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)); 243 if (!errbuf) { 244 SDL_TLSSet(tls_errbuf, NULL, NULL); 245 return &SDL_global_errbuf; 246 } 247 SDL_zerop(errbuf); 248 SDL_TLSSet(tls_errbuf, errbuf, SDL_free); 249 } 250 return errbuf; 251} 252 253 254/* Arguments and callback to setup and run the user thread function */ 255typedef struct 256{ 257 int (SDLCALL * func) (void *); 258 void *data; 259 SDL_Thread *info; 260 SDL_sem *wait; 261} thread_args; 262 263void 264SDL_RunThread(void *data) 265{ 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; 271 272 /* Perform any system-dependent setup - this function may not fail */ 273 SDL_SYS_SetupThread(thread->name); 274 275 /* Get the thread id */ 276 thread->threadid = SDL_ThreadID(); 277 278 /* Wake up the parent thread */ 279 SDL_SemPost(args->wait); 280 281 /* Run the function */ 282 *statusloc = userfunc(userdata); 283 284 /* Clean up thread-local storage */ 285 SDL_TLSCleanup(); 286 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)) { 291 if (thread->name) { 292 SDL_free(thread->name); 293 } 294 SDL_free(thread); 295 } 296 } 297} 298 299#ifdef SDL_CreateThread 300#undef SDL_CreateThread 301#endif 302#if SDL_DYNAMIC_API 303#define SDL_CreateThread SDL_CreateThread_REAL 304#endif 305 306#ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD 307DECLSPEC SDL_Thread *SDLCALL 308SDL_CreateThread(int (SDLCALL * fn) (void *), 309 const char *name, void *data, 310 pfnSDL_CurrentBeginThread pfnBeginThread, 311 pfnSDL_CurrentEndThread pfnEndThread) 312#else 313DECLSPEC SDL_Thread *SDLCALL 314SDL_CreateThread(int (SDLCALL * fn) (void *), 315 const char *name, void *data) 316#endif 317{ 318 SDL_Thread *thread; 319 thread_args *args; 320 int ret; 321 322 /* Allocate memory for the thread info structure */ 323 thread = (SDL_Thread *) SDL_malloc(sizeof(*thread)); 324 if (thread == NULL) { 325 SDL_OutOfMemory(); 326 return (NULL); 327 } 328 SDL_zerop(thread); 329 thread->status = -1; 330 SDL_AtomicSet(&thread->state, SDL_THREAD_STATE_ALIVE); 331 332 /* Set up the arguments for the thread */ 333 if (name != NULL) { 334 thread->name = SDL_strdup(name); 335 if (thread->name == NULL) { 336 SDL_OutOfMemory(); 337 SDL_free(thread); 338 return (NULL); 339 } 340 } 341 342 /* Set up the arguments for the thread */ 343 args = (thread_args *) SDL_malloc(sizeof(*args)); 344 if (args == NULL) { 345 SDL_OutOfMemory(); 346 if (thread->name) { 347 SDL_free(thread->name); 348 } 349 SDL_free(thread); 350 return (NULL); 351 } 352 args->func = fn; 353 args->data = data; 354 args->info = thread; 355 args->wait = SDL_CreateSemaphore(0); 356 if (args->wait == NULL) { 357 if (thread->name) { 358 SDL_free(thread->name); 359 } 360 SDL_free(thread); 361 SDL_free(args); 362 return (NULL); 363 } 364 365 /* Create the thread and go! */ 366#ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD 367 ret = SDL_SYS_CreateThread(thread, args, pfnBeginThread, pfnEndThread); 368#else 369 ret = SDL_SYS_CreateThread(thread, args); 370#endif 371 if (ret >= 0) { 372 /* Wait for the thread function to use arguments */ 373 SDL_SemWait(args->wait); 374 } else { 375 /* Oops, failed. Gotta free everything */ 376 if (thread->name) { 377 SDL_free(thread->name); 378 } 379 SDL_free(thread); 380 thread = NULL; 381 } 382 SDL_DestroySemaphore(args->wait); 383 SDL_free(args); 384 385 /* Everything is running now */ 386 return (thread); 387} 388 389SDL_threadID 390SDL_GetThreadID(SDL_Thread * thread) 391{ 392 SDL_threadID id; 393 394 if (thread) { 395 id = thread->threadid; 396 } else { 397 id = SDL_ThreadID(); 398 } 399 return id; 400} 401 402const char * 403SDL_GetThreadName(SDL_Thread * thread) 404{ 405 if (thread) { 406 return thread->name; 407 } else { 408 return NULL; 409 } 410} 411 412int 413SDL_SetThreadPriority(SDL_ThreadPriority priority) 414{ 415 return SDL_SYS_SetThreadPriority(priority); 416} 417 418void 419SDL_WaitThread(SDL_Thread * thread, int *status) 420{ 421 if (thread) { 422 SDL_SYS_WaitThread(thread); 423 if (status) { 424 *status = thread->status; 425 } 426 if (thread->name) { 427 SDL_free(thread->name); 428 } 429 SDL_free(thread); 430 } 431} 432 433void 434SDL_DetachThread(SDL_Thread * thread) 435{ 436 if (!thread) { 437 return; 438 } 439 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); 443 } else { 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. */ 450 } else { 451 SDL_assert(0 && "Unexpected thread state"); 452 } 453 } 454} 455 456/* vi: set ts=4 sw=4 expandtab: */