Simple Directmedia Layer

Cleaned up thread state handling and added thread object validation

Changed files
+54 -34
include
src
+11 -14
include/SDL3/SDL_thread.h
··· 105 105 /** 106 106 * The SDL thread state. 107 107 * 108 - * SDL stores the current state of a thread in an atomic int. The current 109 - * state of a thread can be checked by calling SDL_GetThreadState. 108 + * The current state of a thread can be checked by calling SDL_GetThreadState. 110 109 * 111 110 * \since This enum is available since SDL 3.1.3. 112 111 * ··· 114 113 */ 115 114 typedef enum SDL_ThreadState 116 115 { 117 - SDL_THREAD_STATE_ALIVE, 118 - SDL_THREAD_STATE_DETACHED, 119 - SDL_THREAD_STATE_ZOMBIE, 120 - SDL_THREAD_STATE_CLEANED, 116 + SDL_THREAD_UNKNOWN, /**< The thread is not valid */ 117 + SDL_THREAD_ALIVE, /**< The thread is currently running */ 118 + SDL_THREAD_DETACHED, /**< The thread is detached and can't be waited on */ 119 + SDL_THREAD_COMPLETE, /**< The thread has finished and should be cleaned up with SDL_WaitThread() */ 121 120 } SDL_ThreadState; 122 121 123 122 /** ··· 408 407 /** 409 408 * Wait for a thread to finish. 410 409 * 411 - * Threads that haven't been detached will remain (as a "zombie") until this 410 + * Threads that haven't been detached will remain until this 412 411 * function cleans them up. Not doing so is a resource leak. 413 412 * 414 413 * Once a thread has been cleaned up through this function, the SDL_Thread 415 414 * that references it becomes invalid and should not be referenced again. As 416 415 * such, only one thread may call SDL_WaitThread() on another. 417 416 * 418 - * The return code for the thread function is placed in the area pointed to by 417 + * The return code from the thread function is placed in the area pointed to by 419 418 * `status`, if `status` is not NULL. 420 419 * 421 420 * You may not wait on a thread that has been used in a call to ··· 429 428 * 430 429 * \param thread the SDL_Thread pointer that was returned from the 431 430 * SDL_CreateThread() call that started this thread. 432 - * \param status pointer to an integer that will receive the value returned 433 - * from the thread function by its 'return', or NULL to not 434 - * receive such value back. 431 + * \param status a pointer filled in with the value returned 432 + * from the thread function by its 'return', or -1 if the thread has been detached or isn't valid, may be NULL. 435 433 * 436 434 * \since This function is available since SDL 3.1.3. 437 435 * ··· 443 441 /** 444 442 * Get the current state of a thread. 445 443 * 446 - * \param thread the thread whose status you want to check. 447 - * \returns the current state of a thread as defined in the SDL_ThreadState 448 - * enum. 444 + * \param thread the thread to query. 445 + * \returns the current state of a thread, or SDL_THREAD_UNKNOWN if the thread isn't valid. 449 446 * 450 447 * \since This function is available since SDL 3.2.0. 451 448 *
+3
src/SDL_utils.c
··· 214 214 case SDL_OBJECT_TYPE_HIDAPI_JOYSTICK: 215 215 type = "hidapi joystick"; 216 216 break; 217 + case SDL_OBJECT_TYPE_THREAD: 218 + type = "thread"; 219 + break; 217 220 default: 218 221 type = "unknown object"; 219 222 break;
+1
src/SDL_utils_c.h
··· 60 60 SDL_OBJECT_TYPE_SENSOR, 61 61 SDL_OBJECT_TYPE_HIDAPI_DEVICE, 62 62 SDL_OBJECT_TYPE_HIDAPI_JOYSTICK, 63 + SDL_OBJECT_TYPE_THREAD, 63 64 64 65 } SDL_ObjectType; 65 66
+37 -18
src/thread/SDL_thread.c
··· 306 306 #endif // SDL_THREADS_DISABLED 307 307 } 308 308 309 + static bool ThreadValid(SDL_Thread *thread) 310 + { 311 + return SDL_ObjectValid(thread, SDL_OBJECT_TYPE_THREAD); 312 + } 313 + 309 314 void SDL_RunThread(SDL_Thread *thread) 310 315 { 311 316 void *userdata = thread->userdata; ··· 326 331 SDL_CleanupTLS(); 327 332 328 333 // Mark us as ready to be joined (or detached) 329 - if (!SDL_CompareAndSwapAtomicInt(&thread->state, SDL_THREAD_STATE_ALIVE, SDL_THREAD_STATE_ZOMBIE)) { 334 + if (!SDL_CompareAndSwapAtomicInt(&thread->state, SDL_THREAD_ALIVE, SDL_THREAD_COMPLETE)) { 330 335 // Clean up if something already detached us. 331 - if (SDL_CompareAndSwapAtomicInt(&thread->state, SDL_THREAD_STATE_DETACHED, SDL_THREAD_STATE_CLEANED)) { 336 + if (SDL_GetThreadState(thread) == SDL_THREAD_DETACHED) { 337 + SDL_SetObjectValid(thread, SDL_OBJECT_TYPE_THREAD, false); 332 338 SDL_free(thread->name); // Can't free later, we've already cleaned up TLS 333 339 SDL_free(thread); 334 340 } ··· 364 370 return NULL; 365 371 } 366 372 thread->status = -1; 367 - SDL_SetAtomicInt(&thread->state, SDL_THREAD_STATE_ALIVE); 373 + SDL_SetAtomicInt(&thread->state, SDL_THREAD_ALIVE); 368 374 369 375 // Set up the arguments for the thread 370 376 if (name) { ··· 378 384 thread->userfunc = fn; 379 385 thread->userdata = userdata; 380 386 thread->stacksize = stacksize; 387 + 388 + SDL_SetObjectValid(thread, SDL_OBJECT_TYPE_THREAD, true); 381 389 382 390 // Create the thread and go! 383 391 if (!SDL_SYS_CreateThread(thread, pfnBeginThread, pfnEndThread)) { 384 392 // Oops, failed. Gotta free everything 393 + SDL_SetObjectValid(thread, SDL_OBJECT_TYPE_THREAD, false); 385 394 SDL_free(thread->name); 386 395 SDL_free(thread); 387 396 thread = NULL; ··· 420 429 421 430 SDL_ThreadID SDL_GetThreadID(SDL_Thread *thread) 422 431 { 423 - SDL_ThreadID id; 432 + SDL_ThreadID id = 0; 424 433 425 434 if (thread) { 426 - id = thread->threadid; 435 + if (ThreadValid(thread)) { 436 + id = thread->threadid; 437 + } 427 438 } else { 428 439 id = SDL_GetCurrentThreadID(); 429 440 } ··· 432 443 433 444 const char *SDL_GetThreadName(SDL_Thread *thread) 434 445 { 435 - if (thread) { 446 + if (ThreadValid(thread)) { 436 447 return SDL_GetPersistentString(thread->name); 437 448 } else { 438 449 return NULL; ··· 446 457 447 458 void SDL_WaitThread(SDL_Thread *thread, int *status) 448 459 { 449 - if (thread) { 450 - SDL_SYS_WaitThread(thread); 460 + if (!ThreadValid(thread) || SDL_GetThreadState(thread) == SDL_THREAD_DETACHED) { 451 461 if (status) { 452 - *status = thread->status; 462 + *status = -1; 453 463 } 454 - SDL_free(thread->name); 455 - SDL_free(thread); 464 + return; 456 465 } 466 + 467 + SDL_SYS_WaitThread(thread); 468 + if (status) { 469 + *status = thread->status; 470 + } 471 + SDL_SetObjectValid(thread, SDL_OBJECT_TYPE_THREAD, false); 472 + SDL_free(thread->name); 473 + SDL_free(thread); 457 474 } 458 475 459 476 SDL_ThreadState SDL_GetThreadState(SDL_Thread *thread) 460 477 { 478 + if (!ThreadValid(thread)) { 479 + return SDL_THREAD_UNKNOWN; 480 + } 481 + 461 482 return (SDL_ThreadState)SDL_GetAtomicInt(&thread->state); 462 483 } 463 484 464 485 void SDL_DetachThread(SDL_Thread *thread) 465 486 { 466 - if (!thread) { 487 + if (!ThreadValid(thread)) { 467 488 return; 468 489 } 469 490 470 491 // Grab dibs if the state is alive+joinable. 471 - if (SDL_CompareAndSwapAtomicInt(&thread->state, SDL_THREAD_STATE_ALIVE, SDL_THREAD_STATE_DETACHED)) { 492 + if (SDL_CompareAndSwapAtomicInt(&thread->state, SDL_THREAD_ALIVE, SDL_THREAD_DETACHED)) { 472 493 SDL_SYS_DetachThread(thread); 473 494 } else { 474 495 // all other states are pretty final, see where we landed. 475 - const int thread_state = SDL_GetAtomicInt(&thread->state); 476 - if ((thread_state == SDL_THREAD_STATE_DETACHED) || (thread_state == SDL_THREAD_STATE_CLEANED)) { 496 + SDL_ThreadState thread_state = SDL_GetThreadState(thread); 497 + if (thread_state == SDL_THREAD_DETACHED) { 477 498 return; // already detached (you shouldn't call this twice!) 478 - } else if (thread_state == SDL_THREAD_STATE_ZOMBIE) { 499 + } else if (thread_state == SDL_THREAD_COMPLETE) { 479 500 SDL_WaitThread(thread, NULL); // already done, clean it up. 480 - } else { 481 - SDL_assert(0 && "Unexpected thread state"); 482 501 } 483 502 } 484 503 }
+1 -1
src/thread/SDL_thread_c.h
··· 50 50 SDL_ThreadID threadid; 51 51 SYS_ThreadHandle handle; 52 52 int status; 53 - SDL_AtomicInt state; /* SDL_THREAD_STATE_* */ 53 + SDL_AtomicInt state; /* SDL_ThreadState */ 54 54 SDL_error errbuf; 55 55 char *name; 56 56 size_t stacksize; // 0 for default, >0 for user-specified stack size.
+1 -1
src/thread/n3ds/SDL_systhread.c
··· 126 126 Detached threads can be waited on, but should NOT be cleaned manually 127 127 as it would result in a fatal error. 128 128 */ 129 - if (R_SUCCEEDED(res) && SDL_GetAtomicInt(&thread->state) != SDL_THREAD_STATE_DETACHED) { 129 + if (R_SUCCEEDED(res) && SDL_GetThreadState(thread) != SDL_THREAD_DETACHED) { 130 130 threadFree(thread->handle); 131 131 } 132 132 }