Atlas - SDL_thread.c

Home / ext / SDL2 / src / thread Lines: 1 | Size: 14081 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)]
[FILE BEGIN]
1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2018 Sam Lantinga <[email protected]> 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_hints.h" 30#include "../SDL_error_c.h" 31 32 33SDL_TLSID 34SDL_TLSCreate() 35{ 36 static SDL_atomic_t SDL_tls_id; 37 return SDL_AtomicIncRef(&SDL_tls_id)+1; 38} 39 40void * 41SDL_TLSGet(SDL_TLSID id) 42{ 43 SDL_TLSData *storage; 44 45 storage = SDL_SYS_GetTLSData(); 46 if (!storage || id == 0 || id > storage->limit) { 47 return NULL; 48 } 49 return storage->array[id-1].data; 50} 51 52int 53SDL_TLSSet(SDL_TLSID id, const void *value, void (SDLCALL *destructor)(void *)) 54{ 55 SDL_TLSData *storage; 56 57 if (id == 0) { 58 return SDL_InvalidParamError("id"); 59 } 60 61 storage = SDL_SYS_GetTLSData(); 62 if (!storage || (id > storage->limit)) { 63 unsigned int i, oldlimit, newlimit; 64 65 oldlimit = storage ? storage->limit : 0; 66 newlimit = (id + TLS_ALLOC_CHUNKSIZE); 67 storage = (SDL_TLSData *)SDL_realloc(storage, sizeof(*storage)+(newlimit-1)*sizeof(storage->array[0])); 68 if (!storage) { 69 return SDL_OutOfMemory(); 70 } 71 storage->limit = newlimit; 72 for (i = oldlimit; i < newlimit; ++i) { 73 storage->array[i].data = NULL; 74 storage->array[i].destructor = NULL; 75 } 76 if (SDL_SYS_SetTLSData(storage) != 0) { 77 return -1; 78 } 79 } 80 81 storage->array[id-1].data = SDL_const_cast(void*, value); 82 storage->array[id-1].destructor = destructor; 83 return 0; 84} 85 86static void 87SDL_TLSCleanup() 88{ 89 SDL_TLSData *storage; 90 91 storage = SDL_SYS_GetTLSData(); 92 if (storage) { 93 unsigned int i; 94 for (i = 0; i < storage->limit; ++i) { 95 if (storage->array[i].destructor) { 96 storage->array[i].destructor(storage->array[i].data); 97 } 98 } 99 SDL_SYS_SetTLSData(NULL); 100 SDL_free(storage); 101 } 102} 103 104 105/* This is a generic implementation of thread-local storage which doesn't 106 require additional OS support. 107 108 It is not especially efficient and doesn't clean up thread-local storage 109 as threads exit. If there is a real OS that doesn't support thread-local 110 storage this implementation should be improved to be production quality. 111*/ 112 113typedef struct SDL_TLSEntry { 114 SDL_threadID thread; 115 SDL_TLSData *storage; 116 struct SDL_TLSEntry *next; 117} SDL_TLSEntry; 118 119static SDL_mutex *SDL_generic_TLS_mutex; 120static SDL_TLSEntry *SDL_generic_TLS; 121 122 123SDL_TLSData * 124SDL_Generic_GetTLSData(void) 125{ 126 SDL_threadID thread = SDL_ThreadID(); 127 SDL_TLSEntry *entry; 128 SDL_TLSData *storage = NULL; 129 130#if !SDL_THREADS_DISABLED 131 if (!SDL_generic_TLS_mutex) { 132 static SDL_SpinLock tls_lock; 133 SDL_AtomicLock(&tls_lock); 134 if (!SDL_generic_TLS_mutex) { 135 SDL_mutex *mutex = SDL_CreateMutex(); 136 SDL_MemoryBarrierRelease(); 137 SDL_generic_TLS_mutex = mutex; 138 if (!SDL_generic_TLS_mutex) { 139 SDL_AtomicUnlock(&tls_lock); 140 return NULL; 141 } 142 } 143 SDL_AtomicUnlock(&tls_lock); 144 } 145#endif /* SDL_THREADS_DISABLED */ 146 147 SDL_MemoryBarrierAcquire(); 148 SDL_LockMutex(SDL_generic_TLS_mutex); 149 for (entry = SDL_generic_TLS; entry; entry = entry->next) { 150 if (entry->thread == thread) { 151 storage = entry->storage; 152 break; 153 } 154 } 155#if !SDL_THREADS_DISABLED 156 SDL_UnlockMutex(SDL_generic_TLS_mutex); 157#endif 158 159 return storage; 160} 161 162int 163SDL_Generic_SetTLSData(SDL_TLSData *storage) 164{ 165 SDL_threadID thread = SDL_ThreadID(); 166 SDL_TLSEntry *prev, *entry; 167 168 /* SDL_Generic_GetTLSData() is always called first, so we can assume SDL_generic_TLS_mutex */ 169 SDL_LockMutex(SDL_generic_TLS_mutex); 170 prev = NULL; 171 for (entry = SDL_generic_TLS; entry; entry = entry->next) { 172 if (entry->thread == thread) { 173 if (storage) { 174 entry->storage = storage; 175 } else { 176 if (prev) { 177 prev->next = entry->next; 178 } else { 179 SDL_generic_TLS = entry->next; 180 } 181 SDL_free(entry); 182 } 183 break; 184 } 185 prev = entry; 186 } 187 if (!entry) { 188 entry = (SDL_TLSEntry *)SDL_malloc(sizeof(*entry)); 189 if (entry) { 190 entry->thread = thread; 191 entry->storage = storage; 192 entry->next = SDL_generic_TLS; 193 SDL_generic_TLS = entry; 194 } 195 } 196 SDL_UnlockMutex(SDL_generic_TLS_mutex); 197 198 if (!entry) { 199 return SDL_OutOfMemory(); 200 } 201 return 0; 202} 203 204/* Routine to get the thread-specific error variable */ 205SDL_error * 206SDL_GetErrBuf(void) 207{ 208 static SDL_SpinLock tls_lock; 209 static SDL_bool tls_being_created; 210 static SDL_TLSID tls_errbuf; 211 static SDL_error SDL_global_errbuf; 212 const SDL_error *ALLOCATION_IN_PROGRESS = (SDL_error *)-1; 213 SDL_error *errbuf; 214 215 /* tls_being_created is there simply to prevent recursion if SDL_TLSCreate() fails. 216 It also means it's possible for another thread to also use SDL_global_errbuf, 217 but that's very unlikely and hopefully won't cause issues. 218 */ 219 if (!tls_errbuf && !tls_being_created) { 220 SDL_AtomicLock(&tls_lock); 221 if (!tls_errbuf) { 222 SDL_TLSID slot; 223 tls_being_created = SDL_TRUE; 224 slot = SDL_TLSCreate(); 225 tls_being_created = SDL_FALSE; 226 SDL_MemoryBarrierRelease(); 227 tls_errbuf = slot; 228 } 229 SDL_AtomicUnlock(&tls_lock); 230 } 231 if (!tls_errbuf) { 232 return &SDL_global_errbuf; 233 } 234 235 SDL_MemoryBarrierAcquire(); 236 errbuf = (SDL_error *)SDL_TLSGet(tls_errbuf); 237 if (errbuf == ALLOCATION_IN_PROGRESS) { 238 return &SDL_global_errbuf; 239 } 240 if (!errbuf) { 241 /* Mark that we're in the middle of allocating our buffer */ 242 SDL_TLSSet(tls_errbuf, ALLOCATION_IN_PROGRESS, NULL); 243 errbuf = (SDL_error *)SDL_malloc(sizeof(*errbuf)); 244 if (!errbuf) { 245 SDL_TLSSet(tls_errbuf, NULL, NULL); 246 return &SDL_global_errbuf; 247 } 248 SDL_zerop(errbuf); 249 SDL_TLSSet(tls_errbuf, errbuf, SDL_free); 250 } 251 return errbuf; 252} 253 254 255/* Arguments and callback to setup and run the user thread function */ 256typedef struct 257{ 258 int (SDLCALL * func) (void *); 259 void *data; 260 SDL_Thread *info; 261 SDL_sem *wait; 262} thread_args; 263 264void 265SDL_RunThread(void *data) 266{ 267 thread_args *args = (thread_args *) data; 268 int (SDLCALL * userfunc) (void *) = args->func; 269 void *userdata = args->data; 270 SDL_Thread *thread = args->info; 271 int *statusloc = &thread->status; 272 273 /* Perform any system-dependent setup - this function may not fail */ 274 SDL_SYS_SetupThread(thread->name); 275 276 /* Get the thread id */ 277 thread->threadid = SDL_ThreadID(); 278 279 /* Wake up the parent thread */ 280 SDL_SemPost(args->wait); 281 282 /* Run the function */ 283 *statusloc = userfunc(userdata); 284 285 /* Clean up thread-local storage */ 286 SDL_TLSCleanup(); 287 288 /* Mark us as ready to be joined (or detached) */ 289 if (!SDL_AtomicCAS(&thread->state, SDL_THREAD_STATE_ALIVE, SDL_THREAD_STATE_ZOMBIE)) { 290 /* Clean up if something already detached us. */ 291 if (SDL_AtomicCAS(&thread->state, SDL_THREAD_STATE_DETACHED, SDL_THREAD_STATE_CLEANED)) { 292 if (thread->name) { 293 SDL_free(thread->name); 294 } 295 SDL_free(thread); 296 } 297 } 298} 299 300#ifdef SDL_CreateThread 301#undef SDL_CreateThread 302#endif 303#if SDL_DYNAMIC_API 304#define SDL_CreateThread SDL_CreateThread_REAL 305#endif 306 307#ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD 308static SDL_Thread * 309SDL_CreateThreadWithStackSize(int (SDLCALL * fn) (void *), 310 const char *name, const size_t stacksize, void *data, 311 pfnSDL_CurrentBeginThread pfnBeginThread, 312 pfnSDL_CurrentEndThread pfnEndThread) 313#else 314static SDL_Thread * 315SDL_CreateThreadWithStackSize(int (SDLCALL * fn) (void *), 316 const char *name, const size_t stacksize, void *data) 317#endif 318{ 319 SDL_Thread *thread; 320 thread_args *args; 321 int ret; 322 323 /* Allocate memory for the thread info structure */ 324 thread = (SDL_Thread *) SDL_malloc(sizeof(*thread)); 325 if (thread == NULL) { 326 SDL_OutOfMemory(); 327 return (NULL); 328 } 329 SDL_zerop(thread); 330 thread->status = -1; 331 SDL_AtomicSet(&thread->state, SDL_THREAD_STATE_ALIVE); 332 333 /* Set up the arguments for the thread */ 334 if (name != NULL) { 335 thread->name = SDL_strdup(name); 336 if (thread->name == NULL) { 337 SDL_OutOfMemory(); 338 SDL_free(thread); 339 return (NULL); 340 } 341 } 342 343 /* Set up the arguments for the thread */ 344 args = (thread_args *) SDL_malloc(sizeof(*args)); 345 if (args == NULL) { 346 SDL_OutOfMemory(); 347 if (thread->name) { 348 SDL_free(thread->name); 349 } 350 SDL_free(thread); 351 return (NULL); 352 } 353 args->func = fn; 354 args->data = data; 355 args->info = thread; 356 args->wait = SDL_CreateSemaphore(0); 357 if (args->wait == NULL) { 358 if (thread->name) { 359 SDL_free(thread->name); 360 } 361 SDL_free(thread); 362 SDL_free(args); 363 return (NULL); 364 } 365 366 thread->stacksize = stacksize; 367 368 /* Create the thread and go! */ 369#ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD 370 ret = SDL_SYS_CreateThread(thread, args, pfnBeginThread, pfnEndThread); 371#else 372 ret = SDL_SYS_CreateThread(thread, args); 373#endif 374 if (ret >= 0) { 375 /* Wait for the thread function to use arguments */ 376 SDL_SemWait(args->wait); 377 } else { 378 /* Oops, failed. Gotta free everything */ 379 if (thread->name) { 380 SDL_free(thread->name); 381 } 382 SDL_free(thread); 383 thread = NULL; 384 } 385 SDL_DestroySemaphore(args->wait); 386 SDL_free(args); 387 388 /* Everything is running now */ 389 return (thread); 390} 391 392#ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD 393DECLSPEC SDL_Thread *SDLCALL 394SDL_CreateThread(int (SDLCALL * fn) (void *), 395 const char *name, void *data, 396 pfnSDL_CurrentBeginThread pfnBeginThread, 397 pfnSDL_CurrentEndThread pfnEndThread) 398#else 399DECLSPEC SDL_Thread *SDLCALL 400SDL_CreateThread(int (SDLCALL * fn) (void *), 401 const char *name, void *data) 402#endif 403{ 404 /* !!! FIXME: in 2.1, just make stackhint part of the usual API. */ 405 const char *stackhint = SDL_GetHint(SDL_HINT_THREAD_STACK_SIZE); 406 size_t stacksize = 0; 407 408 /* If the SDL_HINT_THREAD_STACK_SIZE exists, use it */ 409 if (stackhint != NULL) { 410 char *endp = NULL; 411 const Sint64 hintval = SDL_strtoll(stackhint, &endp, 10); 412 if ((*stackhint != '\0') && (*endp == '\0')) { /* a valid number? */ 413 if (hintval > 0) { /* reject bogus values. */ 414 stacksize = (size_t) hintval; 415 } 416 } 417 } 418 419#ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD 420 return SDL_CreateThreadWithStackSize(fn, name, stacksize, data, pfnBeginThread, pfnEndThread); 421#else 422 return SDL_CreateThreadWithStackSize(fn, name, stacksize, data); 423#endif 424} 425 426SDL_Thread * 427SDL_CreateThreadInternal(int (SDLCALL * fn) (void *), const char *name, 428 const size_t stacksize, void *data) { 429#ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD 430 return SDL_CreateThreadWithStackSize(fn, name, stacksize, data, NULL, NULL); 431#else 432 return SDL_CreateThreadWithStackSize(fn, name, stacksize, data); 433#endif 434} 435 436SDL_threadID 437SDL_GetThreadID(SDL_Thread * thread) 438{ 439 SDL_threadID id; 440 441 if (thread) { 442 id = thread->threadid; 443 } else { 444 id = SDL_ThreadID(); 445 } 446 return id; 447} 448 449const char * 450SDL_GetThreadName(SDL_Thread * thread) 451{ 452 if (thread) { 453 return thread->name; 454 } else { 455 return NULL; 456 } 457} 458 459int 460SDL_SetThreadPriority(SDL_ThreadPriority priority) 461{ 462 return SDL_SYS_SetThreadPriority(priority); 463} 464 465void 466SDL_WaitThread(SDL_Thread * thread, int *status) 467{ 468 if (thread) { 469 SDL_SYS_WaitThread(thread); 470 if (status) { 471 *status = thread->status; 472 } 473 if (thread->name) { 474 SDL_free(thread->name); 475 } 476 SDL_free(thread); 477 } 478} 479 480void 481SDL_DetachThread(SDL_Thread * thread) 482{ 483 if (!thread) { 484 return; 485 } 486 487 /* Grab dibs if the state is alive+joinable. */ 488 if (SDL_AtomicCAS(&thread->state, SDL_THREAD_STATE_ALIVE, SDL_THREAD_STATE_DETACHED)) { 489 SDL_SYS_DetachThread(thread); 490 } else { 491 /* all other states are pretty final, see where we landed. */ 492 const int thread_state = SDL_AtomicGet(&thread->state); 493 if ((thread_state == SDL_THREAD_STATE_DETACHED) || (thread_state == SDL_THREAD_STATE_CLEANED)) { 494 return; /* already detached (you shouldn't call this twice!) */ 495 } else if (thread_state == SDL_THREAD_STATE_ZOMBIE) { 496 SDL_WaitThread(thread, NULL); /* already done, clean it up. */ 497 } else { 498 SDL_assert(0 && "Unexpected thread state"); 499 } 500 } 501} 502 503/* vi: set ts=4 sw=4 expandtab: */ 504
[FILE END]
(C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.