| 1 | /* | 
|---|
| 2 | Simple DirectMedia Layer | 
|---|
| 3 | Copyright (C) 1997-2025 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_thread_c.h" | 
|---|
| 26 | #include "SDL_systhread.h" | 
|---|
| 27 | #include "../SDL_error_c.h" | 
|---|
| 28 |  | 
|---|
| 29 | // The storage is local to the thread, but the IDs are global for the process | 
|---|
| 30 |  | 
|---|
| 31 | static SDL_AtomicInt SDL_tls_allocated; | 
|---|
| 32 | static SDL_AtomicInt SDL_tls_id; | 
|---|
| 33 |  | 
|---|
| 34 | void SDL_InitTLSData(void) | 
|---|
| 35 | { | 
|---|
| 36 | SDL_SYS_InitTLSData(); | 
|---|
| 37 | } | 
|---|
| 38 |  | 
|---|
| 39 | void *SDL_GetTLS(SDL_TLSID *id) | 
|---|
| 40 | { | 
|---|
| 41 | SDL_TLSData *storage; | 
|---|
| 42 | int storage_index; | 
|---|
| 43 |  | 
|---|
| 44 | if (id == NULL) { | 
|---|
| 45 | SDL_InvalidParamError( "id"); | 
|---|
| 46 | return NULL; | 
|---|
| 47 | } | 
|---|
| 48 |  | 
|---|
| 49 | storage_index = SDL_GetAtomicInt(id) - 1; | 
|---|
| 50 | storage = SDL_SYS_GetTLSData(); | 
|---|
| 51 | if (!storage || storage_index < 0 || storage_index >= storage->limit) { | 
|---|
| 52 | return NULL; | 
|---|
| 53 | } | 
|---|
| 54 | return storage->array[storage_index].data; | 
|---|
| 55 | } | 
|---|
| 56 |  | 
|---|
| 57 | bool SDL_SetTLS(SDL_TLSID *id, const void *value, SDL_TLSDestructorCallback destructor) | 
|---|
| 58 | { | 
|---|
| 59 | SDL_TLSData *storage; | 
|---|
| 60 | int storage_index; | 
|---|
| 61 |  | 
|---|
| 62 | if (id == NULL) { | 
|---|
| 63 | return SDL_InvalidParamError( "id"); | 
|---|
| 64 | } | 
|---|
| 65 |  | 
|---|
| 66 | /* Make sure TLS is initialized. | 
|---|
| 67 | * There's a race condition here if you are calling this from non-SDL threads | 
|---|
| 68 | * and haven't called SDL_Init() on your main thread, but such is life. | 
|---|
| 69 | */ | 
|---|
| 70 | SDL_InitTLSData(); | 
|---|
| 71 |  | 
|---|
| 72 | // Get the storage index associated with the ID in a thread-safe way | 
|---|
| 73 | storage_index = SDL_GetAtomicInt(id) - 1; | 
|---|
| 74 | if (storage_index < 0) { | 
|---|
| 75 | int new_id = (SDL_AtomicIncRef(&SDL_tls_id) + 1); | 
|---|
| 76 |  | 
|---|
| 77 | SDL_CompareAndSwapAtomicInt(id, 0, new_id); | 
|---|
| 78 |  | 
|---|
| 79 | /* If there was a race condition we'll have wasted an ID, but every thread | 
|---|
| 80 | * will have the same storage index for this id. | 
|---|
| 81 | */ | 
|---|
| 82 | storage_index = SDL_GetAtomicInt(id) - 1; | 
|---|
| 83 | } | 
|---|
| 84 |  | 
|---|
| 85 | // Get the storage for the current thread | 
|---|
| 86 | storage = SDL_SYS_GetTLSData(); | 
|---|
| 87 | if (!storage || storage_index >= storage->limit) { | 
|---|
| 88 | unsigned int i, oldlimit, newlimit; | 
|---|
| 89 | SDL_TLSData *new_storage; | 
|---|
| 90 |  | 
|---|
| 91 | oldlimit = storage ? storage->limit : 0; | 
|---|
| 92 | newlimit = (storage_index + TLS_ALLOC_CHUNKSIZE); | 
|---|
| 93 | new_storage = (SDL_TLSData *)SDL_realloc(storage, sizeof(*storage) + (newlimit - 1) * sizeof(storage->array[0])); | 
|---|
| 94 | if (!new_storage) { | 
|---|
| 95 | return false; | 
|---|
| 96 | } | 
|---|
| 97 | storage = new_storage; | 
|---|
| 98 | storage->limit = newlimit; | 
|---|
| 99 | for (i = oldlimit; i < newlimit; ++i) { | 
|---|
| 100 | storage->array[i].data = NULL; | 
|---|
| 101 | storage->array[i].destructor = NULL; | 
|---|
| 102 | } | 
|---|
| 103 | if (!SDL_SYS_SetTLSData(storage)) { | 
|---|
| 104 | SDL_free(storage); | 
|---|
| 105 | return false; | 
|---|
| 106 | } | 
|---|
| 107 | SDL_AtomicIncRef(&SDL_tls_allocated); | 
|---|
| 108 | } | 
|---|
| 109 |  | 
|---|
| 110 | storage->array[storage_index].data = SDL_const_cast(void *, value); | 
|---|
| 111 | storage->array[storage_index].destructor = destructor; | 
|---|
| 112 | return true; | 
|---|
| 113 | } | 
|---|
| 114 |  | 
|---|
| 115 | void SDL_CleanupTLS(void) | 
|---|
| 116 | { | 
|---|
| 117 | SDL_TLSData *storage; | 
|---|
| 118 |  | 
|---|
| 119 | // Cleanup the storage for the current thread | 
|---|
| 120 | storage = SDL_SYS_GetTLSData(); | 
|---|
| 121 | if (storage) { | 
|---|
| 122 | int i; | 
|---|
| 123 | for (i = 0; i < storage->limit; ++i) { | 
|---|
| 124 | if (storage->array[i].destructor) { | 
|---|
| 125 | storage->array[i].destructor(storage->array[i].data); | 
|---|
| 126 | } | 
|---|
| 127 | } | 
|---|
| 128 | SDL_SYS_SetTLSData(NULL); | 
|---|
| 129 | SDL_free(storage); | 
|---|
| 130 | (void)SDL_AtomicDecRef(&SDL_tls_allocated); | 
|---|
| 131 | } | 
|---|
| 132 | } | 
|---|
| 133 |  | 
|---|
| 134 | void SDL_QuitTLSData(void) | 
|---|
| 135 | { | 
|---|
| 136 | SDL_CleanupTLS(); | 
|---|
| 137 |  | 
|---|
| 138 | if (SDL_GetAtomicInt(&SDL_tls_allocated) == 0) { | 
|---|
| 139 | SDL_SYS_QuitTLSData(); | 
|---|
| 140 | } else { | 
|---|
| 141 | // Some thread hasn't called SDL_CleanupTLS() | 
|---|
| 142 | } | 
|---|
| 143 | } | 
|---|
| 144 |  | 
|---|
| 145 | /* This is a generic implementation of thread-local storage which doesn't | 
|---|
| 146 | require additional OS support. | 
|---|
| 147 |  | 
|---|
| 148 | It is not especially efficient and doesn't clean up thread-local storage | 
|---|
| 149 | as threads exit.  If there is a real OS that doesn't support thread-local | 
|---|
| 150 | storage this implementation should be improved to be production quality. | 
|---|
| 151 | */ | 
|---|
| 152 |  | 
|---|
| 153 | typedef struct SDL_TLSEntry | 
|---|
| 154 | { | 
|---|
| 155 | SDL_ThreadID thread; | 
|---|
| 156 | SDL_TLSData *storage; | 
|---|
| 157 | struct SDL_TLSEntry *next; | 
|---|
| 158 | } SDL_TLSEntry; | 
|---|
| 159 |  | 
|---|
| 160 | static SDL_Mutex *SDL_generic_TLS_mutex; | 
|---|
| 161 | static SDL_TLSEntry *SDL_generic_TLS; | 
|---|
| 162 |  | 
|---|
| 163 | void SDL_Generic_InitTLSData(void) | 
|---|
| 164 | { | 
|---|
| 165 | if (!SDL_generic_TLS_mutex) { | 
|---|
| 166 | SDL_generic_TLS_mutex = SDL_CreateMutex(); | 
|---|
| 167 | } | 
|---|
| 168 | } | 
|---|
| 169 |  | 
|---|
| 170 | SDL_TLSData *SDL_Generic_GetTLSData(void) | 
|---|
| 171 | { | 
|---|
| 172 | SDL_ThreadID thread = SDL_GetCurrentThreadID(); | 
|---|
| 173 | SDL_TLSEntry *entry; | 
|---|
| 174 | SDL_TLSData *storage = NULL; | 
|---|
| 175 |  | 
|---|
| 176 | SDL_LockMutex(SDL_generic_TLS_mutex); | 
|---|
| 177 | for (entry = SDL_generic_TLS; entry; entry = entry->next) { | 
|---|
| 178 | if (entry->thread == thread) { | 
|---|
| 179 | storage = entry->storage; | 
|---|
| 180 | break; | 
|---|
| 181 | } | 
|---|
| 182 | } | 
|---|
| 183 | SDL_UnlockMutex(SDL_generic_TLS_mutex); | 
|---|
| 184 |  | 
|---|
| 185 | return storage; | 
|---|
| 186 | } | 
|---|
| 187 |  | 
|---|
| 188 | bool SDL_Generic_SetTLSData(SDL_TLSData *data) | 
|---|
| 189 | { | 
|---|
| 190 | SDL_ThreadID thread = SDL_GetCurrentThreadID(); | 
|---|
| 191 | SDL_TLSEntry *prev, *entry; | 
|---|
| 192 | bool result = true; | 
|---|
| 193 |  | 
|---|
| 194 | SDL_LockMutex(SDL_generic_TLS_mutex); | 
|---|
| 195 | prev = NULL; | 
|---|
| 196 | for (entry = SDL_generic_TLS; entry; entry = entry->next) { | 
|---|
| 197 | if (entry->thread == thread) { | 
|---|
| 198 | if (data) { | 
|---|
| 199 | entry->storage = data; | 
|---|
| 200 | } else { | 
|---|
| 201 | if (prev) { | 
|---|
| 202 | prev->next = entry->next; | 
|---|
| 203 | } else { | 
|---|
| 204 | SDL_generic_TLS = entry->next; | 
|---|
| 205 | } | 
|---|
| 206 | SDL_free(entry); | 
|---|
| 207 | } | 
|---|
| 208 | break; | 
|---|
| 209 | } | 
|---|
| 210 | prev = entry; | 
|---|
| 211 | } | 
|---|
| 212 | if (!entry && data) { | 
|---|
| 213 | entry = (SDL_TLSEntry *)SDL_malloc(sizeof(*entry)); | 
|---|
| 214 | if (entry) { | 
|---|
| 215 | entry->thread = thread; | 
|---|
| 216 | entry->storage = data; | 
|---|
| 217 | entry->next = SDL_generic_TLS; | 
|---|
| 218 | SDL_generic_TLS = entry; | 
|---|
| 219 | } else { | 
|---|
| 220 | result = false; | 
|---|
| 221 | } | 
|---|
| 222 | } | 
|---|
| 223 | SDL_UnlockMutex(SDL_generic_TLS_mutex); | 
|---|
| 224 |  | 
|---|
| 225 | return result; | 
|---|
| 226 | } | 
|---|
| 227 |  | 
|---|
| 228 | void SDL_Generic_QuitTLSData(void) | 
|---|
| 229 | { | 
|---|
| 230 | SDL_TLSEntry *entry; | 
|---|
| 231 |  | 
|---|
| 232 | // This should have been cleaned up by the time we get here | 
|---|
| 233 | SDL_assert(!SDL_generic_TLS); | 
|---|
| 234 | if (SDL_generic_TLS) { | 
|---|
| 235 | SDL_LockMutex(SDL_generic_TLS_mutex); | 
|---|
| 236 | for (entry = SDL_generic_TLS; entry; ) { | 
|---|
| 237 | SDL_TLSEntry *next = entry->next; | 
|---|
| 238 | SDL_free(entry->storage); | 
|---|
| 239 | SDL_free(entry); | 
|---|
| 240 | entry = next; | 
|---|
| 241 | } | 
|---|
| 242 | SDL_generic_TLS = NULL; | 
|---|
| 243 | SDL_UnlockMutex(SDL_generic_TLS_mutex); | 
|---|
| 244 | } | 
|---|
| 245 |  | 
|---|
| 246 | if (SDL_generic_TLS_mutex) { | 
|---|
| 247 | SDL_DestroyMutex(SDL_generic_TLS_mutex); | 
|---|
| 248 | SDL_generic_TLS_mutex = NULL; | 
|---|
| 249 | } | 
|---|
| 250 | } | 
|---|
| 251 |  | 
|---|
| 252 | // Non-thread-safe global error variable | 
|---|
| 253 | static SDL_error *SDL_GetStaticErrBuf(void) | 
|---|
| 254 | { | 
|---|
| 255 | static SDL_error SDL_global_error; | 
|---|
| 256 | static char SDL_global_error_str[128]; | 
|---|
| 257 | SDL_global_error.str = SDL_global_error_str; | 
|---|
| 258 | SDL_global_error.len = sizeof(SDL_global_error_str); | 
|---|
| 259 | return &SDL_global_error; | 
|---|
| 260 | } | 
|---|
| 261 |  | 
|---|
| 262 | #ifndef SDL_THREADS_DISABLED | 
|---|
| 263 | static void SDLCALL SDL_FreeErrBuf(void *data) | 
|---|
| 264 | { | 
|---|
| 265 | SDL_error *errbuf = (SDL_error *)data; | 
|---|
| 266 |  | 
|---|
| 267 | if (errbuf->str) { | 
|---|
| 268 | errbuf->free_func(errbuf->str); | 
|---|
| 269 | } | 
|---|
| 270 | errbuf->free_func(errbuf); | 
|---|
| 271 | } | 
|---|
| 272 | #endif | 
|---|
| 273 |  | 
|---|
| 274 | // Routine to get the thread-specific error variable | 
|---|
| 275 | SDL_error *SDL_GetErrBuf(bool create) | 
|---|
| 276 | { | 
|---|
| 277 | #ifdef SDL_THREADS_DISABLED | 
|---|
| 278 | return SDL_GetStaticErrBuf(); | 
|---|
| 279 | #else | 
|---|
| 280 | static SDL_TLSID tls_errbuf; | 
|---|
| 281 | SDL_error *errbuf; | 
|---|
| 282 |  | 
|---|
| 283 | errbuf = (SDL_error *)SDL_GetTLS(&tls_errbuf); | 
|---|
| 284 | if (!errbuf) { | 
|---|
| 285 | if (!create) { | 
|---|
| 286 | return NULL; | 
|---|
| 287 | } | 
|---|
| 288 |  | 
|---|
| 289 | /* Get the original memory functions for this allocation because the lifetime | 
|---|
| 290 | * of the error buffer may span calls to SDL_SetMemoryFunctions() by the app | 
|---|
| 291 | */ | 
|---|
| 292 | SDL_realloc_func realloc_func; | 
|---|
| 293 | SDL_free_func free_func; | 
|---|
| 294 | SDL_GetOriginalMemoryFunctions(NULL, NULL, &realloc_func, &free_func); | 
|---|
| 295 |  | 
|---|
| 296 | errbuf = (SDL_error *)realloc_func(NULL, sizeof(*errbuf)); | 
|---|
| 297 | if (!errbuf) { | 
|---|
| 298 | return SDL_GetStaticErrBuf(); | 
|---|
| 299 | } | 
|---|
| 300 | SDL_zerop(errbuf); | 
|---|
| 301 | errbuf->realloc_func = realloc_func; | 
|---|
| 302 | errbuf->free_func = free_func; | 
|---|
| 303 | SDL_SetTLS(&tls_errbuf, errbuf, SDL_FreeErrBuf); | 
|---|
| 304 | } | 
|---|
| 305 | return errbuf; | 
|---|
| 306 | #endif // SDL_THREADS_DISABLED | 
|---|
| 307 | } | 
|---|
| 308 |  | 
|---|
| 309 | static bool ThreadValid(SDL_Thread *thread) | 
|---|
| 310 | { | 
|---|
| 311 | return SDL_ObjectValid(thread, SDL_OBJECT_TYPE_THREAD); | 
|---|
| 312 | } | 
|---|
| 313 |  | 
|---|
| 314 | void SDL_RunThread(SDL_Thread *thread) | 
|---|
| 315 | { | 
|---|
| 316 | void *userdata = thread->userdata; | 
|---|
| 317 | int(SDLCALL *userfunc)(void *) = thread->userfunc; | 
|---|
| 318 |  | 
|---|
| 319 | int *statusloc = &thread->status; | 
|---|
| 320 |  | 
|---|
| 321 | // Perform any system-dependent setup - this function may not fail | 
|---|
| 322 | SDL_SYS_SetupThread(thread->name); | 
|---|
| 323 |  | 
|---|
| 324 | // Get the thread id | 
|---|
| 325 | thread->threadid = SDL_GetCurrentThreadID(); | 
|---|
| 326 |  | 
|---|
| 327 | // Run the function | 
|---|
| 328 | *statusloc = userfunc(userdata); | 
|---|
| 329 |  | 
|---|
| 330 | // Clean up thread-local storage | 
|---|
| 331 | SDL_CleanupTLS(); | 
|---|
| 332 |  | 
|---|
| 333 | // Mark us as ready to be joined (or detached) | 
|---|
| 334 | if (!SDL_CompareAndSwapAtomicInt(&thread->state, SDL_THREAD_ALIVE, SDL_THREAD_COMPLETE)) { | 
|---|
| 335 | // Clean up if something already detached us. | 
|---|
| 336 | if (SDL_GetThreadState(thread) == SDL_THREAD_DETACHED) { | 
|---|
| 337 | SDL_free(thread->name); // Can't free later, we've already cleaned up TLS | 
|---|
| 338 | SDL_free(thread); | 
|---|
| 339 | } | 
|---|
| 340 | } | 
|---|
| 341 | } | 
|---|
| 342 |  | 
|---|
| 343 | SDL_Thread *SDL_CreateThreadWithPropertiesRuntime(SDL_PropertiesID props, | 
|---|
| 344 | SDL_FunctionPointer pfnBeginThread, | 
|---|
| 345 | SDL_FunctionPointer pfnEndThread) | 
|---|
| 346 | { | 
|---|
| 347 | // rather than check this in every backend, just make sure it's correct upfront. Only allow non-NULL if Windows, or Microsoft GDK. | 
|---|
| 348 | #if !defined(SDL_PLATFORM_WINDOWS) | 
|---|
| 349 | if (pfnBeginThread || pfnEndThread) { | 
|---|
| 350 | SDL_SetError( "_beginthreadex/_endthreadex not supported on this platform"); | 
|---|
| 351 | return NULL; | 
|---|
| 352 | } | 
|---|
| 353 | #endif | 
|---|
| 354 |  | 
|---|
| 355 | SDL_ThreadFunction fn = (SDL_ThreadFunction) SDL_GetPointerProperty(props, SDL_PROP_THREAD_CREATE_ENTRY_FUNCTION_POINTER, NULL); | 
|---|
| 356 | const char *name = SDL_GetStringProperty(props, SDL_PROP_THREAD_CREATE_NAME_STRING, NULL); | 
|---|
| 357 | const size_t stacksize = (size_t) SDL_GetNumberProperty(props, SDL_PROP_THREAD_CREATE_STACKSIZE_NUMBER, 0); | 
|---|
| 358 | void *userdata = SDL_GetPointerProperty(props, SDL_PROP_THREAD_CREATE_USERDATA_POINTER, NULL); | 
|---|
| 359 |  | 
|---|
| 360 | if (!fn) { | 
|---|
| 361 | SDL_SetError( "Thread entry function is NULL"); | 
|---|
| 362 | return NULL; | 
|---|
| 363 | } | 
|---|
| 364 |  | 
|---|
| 365 | SDL_InitMainThread(); | 
|---|
| 366 |  | 
|---|
| 367 | SDL_Thread *thread = (SDL_Thread *)SDL_calloc(1, sizeof(*thread)); | 
|---|
| 368 | if (!thread) { | 
|---|
| 369 | return NULL; | 
|---|
| 370 | } | 
|---|
| 371 | thread->status = -1; | 
|---|
| 372 | SDL_SetAtomicInt(&thread->state, SDL_THREAD_ALIVE); | 
|---|
| 373 |  | 
|---|
| 374 | // Set up the arguments for the thread | 
|---|
| 375 | if (name) { | 
|---|
| 376 | thread->name = SDL_strdup(name); | 
|---|
| 377 | if (!thread->name) { | 
|---|
| 378 | SDL_free(thread); | 
|---|
| 379 | return NULL; | 
|---|
| 380 | } | 
|---|
| 381 | } | 
|---|
| 382 |  | 
|---|
| 383 | thread->userfunc = fn; | 
|---|
| 384 | thread->userdata = userdata; | 
|---|
| 385 | thread->stacksize = stacksize; | 
|---|
| 386 |  | 
|---|
| 387 | SDL_SetObjectValid(thread, SDL_OBJECT_TYPE_THREAD, true); | 
|---|
| 388 |  | 
|---|
| 389 | // Create the thread and go! | 
|---|
| 390 | if (!SDL_SYS_CreateThread(thread, pfnBeginThread, pfnEndThread)) { | 
|---|
| 391 | // Oops, failed.  Gotta free everything | 
|---|
| 392 | SDL_SetObjectValid(thread, SDL_OBJECT_TYPE_THREAD, false); | 
|---|
| 393 | SDL_free(thread->name); | 
|---|
| 394 | SDL_free(thread); | 
|---|
| 395 | thread = NULL; | 
|---|
| 396 | } | 
|---|
| 397 |  | 
|---|
| 398 | // Everything is running now | 
|---|
| 399 | return thread; | 
|---|
| 400 | } | 
|---|
| 401 |  | 
|---|
| 402 | SDL_Thread *SDL_CreateThreadRuntime(SDL_ThreadFunction fn, | 
|---|
| 403 | const char *name, void *userdata, | 
|---|
| 404 | SDL_FunctionPointer pfnBeginThread, | 
|---|
| 405 | SDL_FunctionPointer pfnEndThread) | 
|---|
| 406 | { | 
|---|
| 407 | const SDL_PropertiesID props = SDL_CreateProperties(); | 
|---|
| 408 | SDL_SetPointerProperty(props, SDL_PROP_THREAD_CREATE_ENTRY_FUNCTION_POINTER, (void *) fn); | 
|---|
| 409 | SDL_SetStringProperty(props, SDL_PROP_THREAD_CREATE_NAME_STRING, name); | 
|---|
| 410 | SDL_SetPointerProperty(props, SDL_PROP_THREAD_CREATE_USERDATA_POINTER, userdata); | 
|---|
| 411 | SDL_Thread *thread = SDL_CreateThreadWithPropertiesRuntime(props, pfnBeginThread, pfnEndThread); | 
|---|
| 412 | SDL_DestroyProperties(props); | 
|---|
| 413 | return thread; | 
|---|
| 414 | } | 
|---|
| 415 |  | 
|---|
| 416 | // internal helper function, not in the public API. | 
|---|
| 417 | SDL_Thread *SDL_CreateThreadWithStackSize(SDL_ThreadFunction fn, const char *name, size_t stacksize, void *userdata) | 
|---|
| 418 | { | 
|---|
| 419 | const SDL_PropertiesID props = SDL_CreateProperties(); | 
|---|
| 420 | SDL_SetPointerProperty(props, SDL_PROP_THREAD_CREATE_ENTRY_FUNCTION_POINTER, (void *) fn); | 
|---|
| 421 | SDL_SetStringProperty(props, SDL_PROP_THREAD_CREATE_NAME_STRING, name); | 
|---|
| 422 | SDL_SetPointerProperty(props, SDL_PROP_THREAD_CREATE_USERDATA_POINTER, userdata); | 
|---|
| 423 | SDL_SetNumberProperty(props, SDL_PROP_THREAD_CREATE_STACKSIZE_NUMBER, (Sint64) stacksize); | 
|---|
| 424 | SDL_Thread *thread = SDL_CreateThreadWithProperties(props); | 
|---|
| 425 | SDL_DestroyProperties(props); | 
|---|
| 426 | return thread; | 
|---|
| 427 | } | 
|---|
| 428 |  | 
|---|
| 429 | SDL_ThreadID SDL_GetThreadID(SDL_Thread *thread) | 
|---|
| 430 | { | 
|---|
| 431 | SDL_ThreadID id = 0; | 
|---|
| 432 |  | 
|---|
| 433 | if (thread) { | 
|---|
| 434 | if (ThreadValid(thread)) { | 
|---|
| 435 | id = thread->threadid; | 
|---|
| 436 | } | 
|---|
| 437 | } else { | 
|---|
| 438 | id = SDL_GetCurrentThreadID(); | 
|---|
| 439 | } | 
|---|
| 440 | return id; | 
|---|
| 441 | } | 
|---|
| 442 |  | 
|---|
| 443 | const char *SDL_GetThreadName(SDL_Thread *thread) | 
|---|
| 444 | { | 
|---|
| 445 | if (ThreadValid(thread)) { | 
|---|
| 446 | return SDL_GetPersistentString(thread->name); | 
|---|
| 447 | } else { | 
|---|
| 448 | return NULL; | 
|---|
| 449 | } | 
|---|
| 450 | } | 
|---|
| 451 |  | 
|---|
| 452 | bool SDL_SetCurrentThreadPriority(SDL_ThreadPriority priority) | 
|---|
| 453 | { | 
|---|
| 454 | return SDL_SYS_SetThreadPriority(priority); | 
|---|
| 455 | } | 
|---|
| 456 |  | 
|---|
| 457 | void SDL_WaitThread(SDL_Thread *thread, int *status) | 
|---|
| 458 | { | 
|---|
| 459 | if (!ThreadValid(thread)) { | 
|---|
| 460 | if (status) { | 
|---|
| 461 | *status = -1; | 
|---|
| 462 | } | 
|---|
| 463 | return; | 
|---|
| 464 | } | 
|---|
| 465 |  | 
|---|
| 466 | SDL_SYS_WaitThread(thread); | 
|---|
| 467 | if (status) { | 
|---|
| 468 | *status = thread->status; | 
|---|
| 469 | } | 
|---|
| 470 | SDL_SetObjectValid(thread, SDL_OBJECT_TYPE_THREAD, false); | 
|---|
| 471 | SDL_free(thread->name); | 
|---|
| 472 | SDL_free(thread); | 
|---|
| 473 | } | 
|---|
| 474 |  | 
|---|
| 475 | SDL_ThreadState SDL_GetThreadState(SDL_Thread *thread) | 
|---|
| 476 | { | 
|---|
| 477 | if (!ThreadValid(thread)) { | 
|---|
| 478 | return SDL_THREAD_UNKNOWN; | 
|---|
| 479 | } | 
|---|
| 480 |  | 
|---|
| 481 | return (SDL_ThreadState)SDL_GetAtomicInt(&thread->state); | 
|---|
| 482 | } | 
|---|
| 483 |  | 
|---|
| 484 | void SDL_DetachThread(SDL_Thread *thread) | 
|---|
| 485 | { | 
|---|
| 486 | if (!ThreadValid(thread)) { | 
|---|
| 487 | return; | 
|---|
| 488 | } | 
|---|
| 489 |  | 
|---|
| 490 | // The thread may vanish at any time, it's no longer valid | 
|---|
| 491 | SDL_SetObjectValid(thread, SDL_OBJECT_TYPE_THREAD, false); | 
|---|
| 492 |  | 
|---|
| 493 | // Grab dibs if the state is alive+joinable. | 
|---|
| 494 | if (SDL_CompareAndSwapAtomicInt(&thread->state, SDL_THREAD_ALIVE, SDL_THREAD_DETACHED)) { | 
|---|
| 495 | SDL_SYS_DetachThread(thread); | 
|---|
| 496 | } else { | 
|---|
| 497 | // all other states are pretty final, see where we landed. | 
|---|
| 498 | SDL_ThreadState thread_state = SDL_GetThreadState(thread); | 
|---|
| 499 | if (thread_state == SDL_THREAD_DETACHED) { | 
|---|
| 500 | return; // already detached (you shouldn't call this twice!) | 
|---|
| 501 | } else if (thread_state == SDL_THREAD_COMPLETE) { | 
|---|
| 502 | SDL_WaitThread(thread, NULL); // already done, clean it up. | 
|---|
| 503 | } | 
|---|
| 504 | } | 
|---|
| 505 | } | 
|---|
| 506 |  | 
|---|
| 507 | void SDL_WaitSemaphore(SDL_Semaphore *sem) | 
|---|
| 508 | { | 
|---|
| 509 | SDL_WaitSemaphoreTimeoutNS(sem, -1); | 
|---|
| 510 | } | 
|---|
| 511 |  | 
|---|
| 512 | bool SDL_TryWaitSemaphore(SDL_Semaphore *sem) | 
|---|
| 513 | { | 
|---|
| 514 | return SDL_WaitSemaphoreTimeoutNS(sem, 0); | 
|---|
| 515 | } | 
|---|
| 516 |  | 
|---|
| 517 | bool SDL_WaitSemaphoreTimeout(SDL_Semaphore *sem, Sint32 timeoutMS) | 
|---|
| 518 | { | 
|---|
| 519 | Sint64 timeoutNS; | 
|---|
| 520 |  | 
|---|
| 521 | if (timeoutMS >= 0) { | 
|---|
| 522 | timeoutNS = SDL_MS_TO_NS(timeoutMS); | 
|---|
| 523 | } else { | 
|---|
| 524 | timeoutNS = -1; | 
|---|
| 525 | } | 
|---|
| 526 | return SDL_WaitSemaphoreTimeoutNS(sem, timeoutNS); | 
|---|
| 527 | } | 
|---|
| 528 |  | 
|---|
| 529 | void SDL_WaitCondition(SDL_Condition *cond, SDL_Mutex *mutex) | 
|---|
| 530 | { | 
|---|
| 531 | SDL_WaitConditionTimeoutNS(cond, mutex, -1); | 
|---|
| 532 | } | 
|---|
| 533 |  | 
|---|
| 534 | bool SDL_WaitConditionTimeout(SDL_Condition *cond, SDL_Mutex *mutex, Sint32 timeoutMS) | 
|---|
| 535 | { | 
|---|
| 536 | Sint64 timeoutNS; | 
|---|
| 537 |  | 
|---|
| 538 | if (timeoutMS >= 0) { | 
|---|
| 539 | timeoutNS = SDL_MS_TO_NS(timeoutMS); | 
|---|
| 540 | } else { | 
|---|
| 541 | timeoutNS = -1; | 
|---|
| 542 | } | 
|---|
| 543 | return SDL_WaitConditionTimeoutNS(cond, mutex, timeoutNS); | 
|---|
| 544 | } | 
|---|
| 545 |  | 
|---|
| 546 | bool SDL_ShouldInit(SDL_InitState *state) | 
|---|
| 547 | { | 
|---|
| 548 | while (SDL_GetAtomicInt(&state->status) != SDL_INIT_STATUS_INITIALIZED) { | 
|---|
| 549 | if (SDL_CompareAndSwapAtomicInt(&state->status, SDL_INIT_STATUS_UNINITIALIZED, SDL_INIT_STATUS_INITIALIZING)) { | 
|---|
| 550 | state->thread = SDL_GetCurrentThreadID(); | 
|---|
| 551 | return true; | 
|---|
| 552 | } | 
|---|
| 553 |  | 
|---|
| 554 | // Wait for the other thread to complete transition | 
|---|
| 555 | SDL_Delay(1); | 
|---|
| 556 | } | 
|---|
| 557 | return false; | 
|---|
| 558 | } | 
|---|
| 559 |  | 
|---|
| 560 | bool SDL_ShouldQuit(SDL_InitState *state) | 
|---|
| 561 | { | 
|---|
| 562 | while (SDL_GetAtomicInt(&state->status) != SDL_INIT_STATUS_UNINITIALIZED) { | 
|---|
| 563 | if (SDL_CompareAndSwapAtomicInt(&state->status, SDL_INIT_STATUS_INITIALIZED, SDL_INIT_STATUS_UNINITIALIZING)) { | 
|---|
| 564 | state->thread = SDL_GetCurrentThreadID(); | 
|---|
| 565 | return true; | 
|---|
| 566 | } | 
|---|
| 567 |  | 
|---|
| 568 | // Wait for the other thread to complete transition | 
|---|
| 569 | SDL_Delay(1); | 
|---|
| 570 | } | 
|---|
| 571 | return false; | 
|---|
| 572 | } | 
|---|
| 573 |  | 
|---|
| 574 | void SDL_SetInitialized(SDL_InitState *state, bool initialized) | 
|---|
| 575 | { | 
|---|
| 576 | SDL_assert(state->thread == SDL_GetCurrentThreadID()); | 
|---|
| 577 |  | 
|---|
| 578 | if (initialized) { | 
|---|
| 579 | SDL_SetAtomicInt(&state->status, SDL_INIT_STATUS_INITIALIZED); | 
|---|
| 580 | } else { | 
|---|
| 581 | SDL_SetAtomicInt(&state->status, SDL_INIT_STATUS_UNINITIALIZED); | 
|---|
| 582 | } | 
|---|
| 583 | } | 
|---|
| 584 |  | 
|---|
| 585 |  | 
|---|