initial commit, 4.5 stable
Some checks failed
🔗 GHA / 📊 Static checks (push) Has been cancelled
🔗 GHA / 🤖 Android (push) Has been cancelled
🔗 GHA / 🍏 iOS (push) Has been cancelled
🔗 GHA / 🐧 Linux (push) Has been cancelled
🔗 GHA / 🍎 macOS (push) Has been cancelled
🔗 GHA / 🏁 Windows (push) Has been cancelled
🔗 GHA / 🌐 Web (push) Has been cancelled

This commit is contained in:
2025-09-16 20:46:46 -04:00
commit 9d30169a8d
13378 changed files with 7050105 additions and 0 deletions

77
thirdparty/sdl/thread/SDL_systhread.h vendored Normal file
View File

@@ -0,0 +1,77 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
// These are functions that need to be implemented by a port of SDL
#ifndef SDL_systhread_h_
#define SDL_systhread_h_
#include "SDL_thread_c.h"
// Set up for C function definitions, even when using C++
#ifdef __cplusplus
extern "C" {
#endif
/* This function creates a thread, passing args to SDL_RunThread(),
saves a system-dependent thread id in thread->id, and returns 0
on success.
*/
extern bool SDL_SYS_CreateThread(SDL_Thread *thread,
SDL_FunctionPointer pfnBeginThread,
SDL_FunctionPointer pfnEndThread);
// This function does any necessary setup in the child thread
extern void SDL_SYS_SetupThread(const char *name);
// This function sets the current thread priority
extern bool SDL_SYS_SetThreadPriority(SDL_ThreadPriority priority);
/* This function waits for the thread to finish and frees any data
allocated by SDL_SYS_CreateThread()
*/
extern void SDL_SYS_WaitThread(SDL_Thread *thread);
// Mark thread as cleaned up as soon as it exits, without joining.
extern void SDL_SYS_DetachThread(SDL_Thread *thread);
// Initialize the global TLS data
extern void SDL_SYS_InitTLSData(void);
// Get the thread local storage for this thread
extern SDL_TLSData *SDL_SYS_GetTLSData(void);
// Set the thread local storage for this thread
extern bool SDL_SYS_SetTLSData(SDL_TLSData *data);
// Quit the global TLS data
extern void SDL_SYS_QuitTLSData(void);
// A helper function for setting up a thread with a stack size.
extern SDL_Thread *SDL_CreateThreadWithStackSize(SDL_ThreadFunction fn, const char *name, size_t stacksize, void *data);
// Ends C function definitions when using C++
#ifdef __cplusplus
}
#endif
#endif // SDL_systhread_h_

584
thirdparty/sdl/thread/SDL_thread.c vendored Normal file
View File

@@ -0,0 +1,584 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
// System independent thread management routines for SDL
#include "SDL_thread_c.h"
#include "SDL_systhread.h"
#include "../SDL_error_c.h"
// The storage is local to the thread, but the IDs are global for the process
static SDL_AtomicInt SDL_tls_allocated;
static SDL_AtomicInt SDL_tls_id;
void SDL_InitTLSData(void)
{
SDL_SYS_InitTLSData();
}
void *SDL_GetTLS(SDL_TLSID *id)
{
SDL_TLSData *storage;
int storage_index;
if (id == NULL) {
SDL_InvalidParamError("id");
return NULL;
}
storage_index = SDL_GetAtomicInt(id) - 1;
storage = SDL_SYS_GetTLSData();
if (!storage || storage_index < 0 || storage_index >= storage->limit) {
return NULL;
}
return storage->array[storage_index].data;
}
bool SDL_SetTLS(SDL_TLSID *id, const void *value, SDL_TLSDestructorCallback destructor)
{
SDL_TLSData *storage;
int storage_index;
if (id == NULL) {
return SDL_InvalidParamError("id");
}
/* Make sure TLS is initialized.
* There's a race condition here if you are calling this from non-SDL threads
* and haven't called SDL_Init() on your main thread, but such is life.
*/
SDL_InitTLSData();
// Get the storage index associated with the ID in a thread-safe way
storage_index = SDL_GetAtomicInt(id) - 1;
if (storage_index < 0) {
int new_id = (SDL_AtomicIncRef(&SDL_tls_id) + 1);
SDL_CompareAndSwapAtomicInt(id, 0, new_id);
/* If there was a race condition we'll have wasted an ID, but every thread
* will have the same storage index for this id.
*/
storage_index = SDL_GetAtomicInt(id) - 1;
}
// Get the storage for the current thread
storage = SDL_SYS_GetTLSData();
if (!storage || storage_index >= storage->limit) {
unsigned int i, oldlimit, newlimit;
SDL_TLSData *new_storage;
oldlimit = storage ? storage->limit : 0;
newlimit = (storage_index + TLS_ALLOC_CHUNKSIZE);
new_storage = (SDL_TLSData *)SDL_realloc(storage, sizeof(*storage) + (newlimit - 1) * sizeof(storage->array[0]));
if (!new_storage) {
return false;
}
storage = new_storage;
storage->limit = newlimit;
for (i = oldlimit; i < newlimit; ++i) {
storage->array[i].data = NULL;
storage->array[i].destructor = NULL;
}
if (!SDL_SYS_SetTLSData(storage)) {
SDL_free(storage);
return false;
}
SDL_AtomicIncRef(&SDL_tls_allocated);
}
storage->array[storage_index].data = SDL_const_cast(void *, value);
storage->array[storage_index].destructor = destructor;
return true;
}
void SDL_CleanupTLS(void)
{
SDL_TLSData *storage;
// Cleanup the storage for the current thread
storage = SDL_SYS_GetTLSData();
if (storage) {
int i;
for (i = 0; i < storage->limit; ++i) {
if (storage->array[i].destructor) {
storage->array[i].destructor(storage->array[i].data);
}
}
SDL_SYS_SetTLSData(NULL);
SDL_free(storage);
(void)SDL_AtomicDecRef(&SDL_tls_allocated);
}
}
void SDL_QuitTLSData(void)
{
SDL_CleanupTLS();
if (SDL_GetAtomicInt(&SDL_tls_allocated) == 0) {
SDL_SYS_QuitTLSData();
} else {
// Some thread hasn't called SDL_CleanupTLS()
}
}
/* This is a generic implementation of thread-local storage which doesn't
require additional OS support.
It is not especially efficient and doesn't clean up thread-local storage
as threads exit. If there is a real OS that doesn't support thread-local
storage this implementation should be improved to be production quality.
*/
typedef struct SDL_TLSEntry
{
SDL_ThreadID thread;
SDL_TLSData *storage;
struct SDL_TLSEntry *next;
} SDL_TLSEntry;
static SDL_Mutex *SDL_generic_TLS_mutex;
static SDL_TLSEntry *SDL_generic_TLS;
void SDL_Generic_InitTLSData(void)
{
if (!SDL_generic_TLS_mutex) {
SDL_generic_TLS_mutex = SDL_CreateMutex();
}
}
SDL_TLSData *SDL_Generic_GetTLSData(void)
{
SDL_ThreadID thread = SDL_GetCurrentThreadID();
SDL_TLSEntry *entry;
SDL_TLSData *storage = NULL;
SDL_LockMutex(SDL_generic_TLS_mutex);
for (entry = SDL_generic_TLS; entry; entry = entry->next) {
if (entry->thread == thread) {
storage = entry->storage;
break;
}
}
SDL_UnlockMutex(SDL_generic_TLS_mutex);
return storage;
}
bool SDL_Generic_SetTLSData(SDL_TLSData *data)
{
SDL_ThreadID thread = SDL_GetCurrentThreadID();
SDL_TLSEntry *prev, *entry;
bool result = true;
SDL_LockMutex(SDL_generic_TLS_mutex);
prev = NULL;
for (entry = SDL_generic_TLS; entry; entry = entry->next) {
if (entry->thread == thread) {
if (data) {
entry->storage = data;
} else {
if (prev) {
prev->next = entry->next;
} else {
SDL_generic_TLS = entry->next;
}
SDL_free(entry);
}
break;
}
prev = entry;
}
if (!entry && data) {
entry = (SDL_TLSEntry *)SDL_malloc(sizeof(*entry));
if (entry) {
entry->thread = thread;
entry->storage = data;
entry->next = SDL_generic_TLS;
SDL_generic_TLS = entry;
} else {
result = false;
}
}
SDL_UnlockMutex(SDL_generic_TLS_mutex);
return result;
}
void SDL_Generic_QuitTLSData(void)
{
SDL_TLSEntry *entry;
// This should have been cleaned up by the time we get here
SDL_assert(!SDL_generic_TLS);
if (SDL_generic_TLS) {
SDL_LockMutex(SDL_generic_TLS_mutex);
for (entry = SDL_generic_TLS; entry; ) {
SDL_TLSEntry *next = entry->next;
SDL_free(entry->storage);
SDL_free(entry);
entry = next;
}
SDL_generic_TLS = NULL;
SDL_UnlockMutex(SDL_generic_TLS_mutex);
}
if (SDL_generic_TLS_mutex) {
SDL_DestroyMutex(SDL_generic_TLS_mutex);
SDL_generic_TLS_mutex = NULL;
}
}
// Non-thread-safe global error variable
static SDL_error *SDL_GetStaticErrBuf(void)
{
static SDL_error SDL_global_error;
static char SDL_global_error_str[128];
SDL_global_error.str = SDL_global_error_str;
SDL_global_error.len = sizeof(SDL_global_error_str);
return &SDL_global_error;
}
#ifndef SDL_THREADS_DISABLED
static void SDLCALL SDL_FreeErrBuf(void *data)
{
SDL_error *errbuf = (SDL_error *)data;
if (errbuf->str) {
errbuf->free_func(errbuf->str);
}
errbuf->free_func(errbuf);
}
#endif
// Routine to get the thread-specific error variable
SDL_error *SDL_GetErrBuf(bool create)
{
#ifdef SDL_THREADS_DISABLED
return SDL_GetStaticErrBuf();
#else
static SDL_TLSID tls_errbuf;
SDL_error *errbuf;
errbuf = (SDL_error *)SDL_GetTLS(&tls_errbuf);
if (!errbuf) {
if (!create) {
return NULL;
}
/* Get the original memory functions for this allocation because the lifetime
* of the error buffer may span calls to SDL_SetMemoryFunctions() by the app
*/
SDL_realloc_func realloc_func;
SDL_free_func free_func;
SDL_GetOriginalMemoryFunctions(NULL, NULL, &realloc_func, &free_func);
errbuf = (SDL_error *)realloc_func(NULL, sizeof(*errbuf));
if (!errbuf) {
return SDL_GetStaticErrBuf();
}
SDL_zerop(errbuf);
errbuf->realloc_func = realloc_func;
errbuf->free_func = free_func;
SDL_SetTLS(&tls_errbuf, errbuf, SDL_FreeErrBuf);
}
return errbuf;
#endif // SDL_THREADS_DISABLED
}
static bool ThreadValid(SDL_Thread *thread)
{
return SDL_ObjectValid(thread, SDL_OBJECT_TYPE_THREAD);
}
void SDL_RunThread(SDL_Thread *thread)
{
void *userdata = thread->userdata;
int(SDLCALL *userfunc)(void *) = thread->userfunc;
int *statusloc = &thread->status;
// Perform any system-dependent setup - this function may not fail
SDL_SYS_SetupThread(thread->name);
// Get the thread id
thread->threadid = SDL_GetCurrentThreadID();
// Run the function
*statusloc = userfunc(userdata);
// Clean up thread-local storage
SDL_CleanupTLS();
// Mark us as ready to be joined (or detached)
if (!SDL_CompareAndSwapAtomicInt(&thread->state, SDL_THREAD_ALIVE, SDL_THREAD_COMPLETE)) {
// Clean up if something already detached us.
if (SDL_GetThreadState(thread) == SDL_THREAD_DETACHED) {
SDL_free(thread->name); // Can't free later, we've already cleaned up TLS
SDL_free(thread);
}
}
}
SDL_Thread *SDL_CreateThreadWithPropertiesRuntime(SDL_PropertiesID props,
SDL_FunctionPointer pfnBeginThread,
SDL_FunctionPointer pfnEndThread)
{
// rather than check this in every backend, just make sure it's correct upfront. Only allow non-NULL if Windows, or Microsoft GDK.
#if !defined(SDL_PLATFORM_WINDOWS)
if (pfnBeginThread || pfnEndThread) {
SDL_SetError("_beginthreadex/_endthreadex not supported on this platform");
return NULL;
}
#endif
SDL_ThreadFunction fn = (SDL_ThreadFunction) SDL_GetPointerProperty(props, SDL_PROP_THREAD_CREATE_ENTRY_FUNCTION_POINTER, NULL);
const char *name = SDL_GetStringProperty(props, SDL_PROP_THREAD_CREATE_NAME_STRING, NULL);
const size_t stacksize = (size_t) SDL_GetNumberProperty(props, SDL_PROP_THREAD_CREATE_STACKSIZE_NUMBER, 0);
void *userdata = SDL_GetPointerProperty(props, SDL_PROP_THREAD_CREATE_USERDATA_POINTER, NULL);
if (!fn) {
SDL_SetError("Thread entry function is NULL");
return NULL;
}
SDL_InitMainThread();
SDL_Thread *thread = (SDL_Thread *)SDL_calloc(1, sizeof(*thread));
if (!thread) {
return NULL;
}
thread->status = -1;
SDL_SetAtomicInt(&thread->state, SDL_THREAD_ALIVE);
// Set up the arguments for the thread
if (name) {
thread->name = SDL_strdup(name);
if (!thread->name) {
SDL_free(thread);
return NULL;
}
}
thread->userfunc = fn;
thread->userdata = userdata;
thread->stacksize = stacksize;
SDL_SetObjectValid(thread, SDL_OBJECT_TYPE_THREAD, true);
// Create the thread and go!
if (!SDL_SYS_CreateThread(thread, pfnBeginThread, pfnEndThread)) {
// Oops, failed. Gotta free everything
SDL_SetObjectValid(thread, SDL_OBJECT_TYPE_THREAD, false);
SDL_free(thread->name);
SDL_free(thread);
thread = NULL;
}
// Everything is running now
return thread;
}
SDL_Thread *SDL_CreateThreadRuntime(SDL_ThreadFunction fn,
const char *name, void *userdata,
SDL_FunctionPointer pfnBeginThread,
SDL_FunctionPointer pfnEndThread)
{
const SDL_PropertiesID props = SDL_CreateProperties();
SDL_SetPointerProperty(props, SDL_PROP_THREAD_CREATE_ENTRY_FUNCTION_POINTER, (void *) fn);
SDL_SetStringProperty(props, SDL_PROP_THREAD_CREATE_NAME_STRING, name);
SDL_SetPointerProperty(props, SDL_PROP_THREAD_CREATE_USERDATA_POINTER, userdata);
SDL_Thread *thread = SDL_CreateThreadWithPropertiesRuntime(props, pfnBeginThread, pfnEndThread);
SDL_DestroyProperties(props);
return thread;
}
// internal helper function, not in the public API.
SDL_Thread *SDL_CreateThreadWithStackSize(SDL_ThreadFunction fn, const char *name, size_t stacksize, void *userdata)
{
const SDL_PropertiesID props = SDL_CreateProperties();
SDL_SetPointerProperty(props, SDL_PROP_THREAD_CREATE_ENTRY_FUNCTION_POINTER, (void *) fn);
SDL_SetStringProperty(props, SDL_PROP_THREAD_CREATE_NAME_STRING, name);
SDL_SetPointerProperty(props, SDL_PROP_THREAD_CREATE_USERDATA_POINTER, userdata);
SDL_SetNumberProperty(props, SDL_PROP_THREAD_CREATE_STACKSIZE_NUMBER, (Sint64) stacksize);
SDL_Thread *thread = SDL_CreateThreadWithProperties(props);
SDL_DestroyProperties(props);
return thread;
}
SDL_ThreadID SDL_GetThreadID(SDL_Thread *thread)
{
SDL_ThreadID id = 0;
if (thread) {
if (ThreadValid(thread)) {
id = thread->threadid;
}
} else {
id = SDL_GetCurrentThreadID();
}
return id;
}
const char *SDL_GetThreadName(SDL_Thread *thread)
{
if (ThreadValid(thread)) {
return SDL_GetPersistentString(thread->name);
} else {
return NULL;
}
}
bool SDL_SetCurrentThreadPriority(SDL_ThreadPriority priority)
{
return SDL_SYS_SetThreadPriority(priority);
}
void SDL_WaitThread(SDL_Thread *thread, int *status)
{
if (!ThreadValid(thread)) {
if (status) {
*status = -1;
}
return;
}
SDL_SYS_WaitThread(thread);
if (status) {
*status = thread->status;
}
SDL_SetObjectValid(thread, SDL_OBJECT_TYPE_THREAD, false);
SDL_free(thread->name);
SDL_free(thread);
}
SDL_ThreadState SDL_GetThreadState(SDL_Thread *thread)
{
if (!ThreadValid(thread)) {
return SDL_THREAD_UNKNOWN;
}
return (SDL_ThreadState)SDL_GetAtomicInt(&thread->state);
}
void SDL_DetachThread(SDL_Thread *thread)
{
if (!ThreadValid(thread)) {
return;
}
// The thread may vanish at any time, it's no longer valid
SDL_SetObjectValid(thread, SDL_OBJECT_TYPE_THREAD, false);
// Grab dibs if the state is alive+joinable.
if (SDL_CompareAndSwapAtomicInt(&thread->state, SDL_THREAD_ALIVE, SDL_THREAD_DETACHED)) {
SDL_SYS_DetachThread(thread);
} else {
// all other states are pretty final, see where we landed.
SDL_ThreadState thread_state = SDL_GetThreadState(thread);
if (thread_state == SDL_THREAD_DETACHED) {
return; // already detached (you shouldn't call this twice!)
} else if (thread_state == SDL_THREAD_COMPLETE) {
SDL_WaitThread(thread, NULL); // already done, clean it up.
}
}
}
void SDL_WaitSemaphore(SDL_Semaphore *sem)
{
SDL_WaitSemaphoreTimeoutNS(sem, -1);
}
bool SDL_TryWaitSemaphore(SDL_Semaphore *sem)
{
return SDL_WaitSemaphoreTimeoutNS(sem, 0);
}
bool SDL_WaitSemaphoreTimeout(SDL_Semaphore *sem, Sint32 timeoutMS)
{
Sint64 timeoutNS;
if (timeoutMS >= 0) {
timeoutNS = SDL_MS_TO_NS(timeoutMS);
} else {
timeoutNS = -1;
}
return SDL_WaitSemaphoreTimeoutNS(sem, timeoutNS);
}
void SDL_WaitCondition(SDL_Condition *cond, SDL_Mutex *mutex)
{
SDL_WaitConditionTimeoutNS(cond, mutex, -1);
}
bool SDL_WaitConditionTimeout(SDL_Condition *cond, SDL_Mutex *mutex, Sint32 timeoutMS)
{
Sint64 timeoutNS;
if (timeoutMS >= 0) {
timeoutNS = SDL_MS_TO_NS(timeoutMS);
} else {
timeoutNS = -1;
}
return SDL_WaitConditionTimeoutNS(cond, mutex, timeoutNS);
}
bool SDL_ShouldInit(SDL_InitState *state)
{
while (SDL_GetAtomicInt(&state->status) != SDL_INIT_STATUS_INITIALIZED) {
if (SDL_CompareAndSwapAtomicInt(&state->status, SDL_INIT_STATUS_UNINITIALIZED, SDL_INIT_STATUS_INITIALIZING)) {
state->thread = SDL_GetCurrentThreadID();
return true;
}
// Wait for the other thread to complete transition
SDL_Delay(1);
}
return false;
}
bool SDL_ShouldQuit(SDL_InitState *state)
{
while (SDL_GetAtomicInt(&state->status) != SDL_INIT_STATUS_UNINITIALIZED) {
if (SDL_CompareAndSwapAtomicInt(&state->status, SDL_INIT_STATUS_INITIALIZED, SDL_INIT_STATUS_UNINITIALIZING)) {
state->thread = SDL_GetCurrentThreadID();
return true;
}
// Wait for the other thread to complete transition
SDL_Delay(1);
}
return false;
}
void SDL_SetInitialized(SDL_InitState *state, bool initialized)
{
SDL_assert(state->thread == SDL_GetCurrentThreadID());
if (initialized) {
SDL_SetAtomicInt(&state->status, SDL_INIT_STATUS_INITIALIZED);
} else {
SDL_SetAtomicInt(&state->status, SDL_INIT_STATUS_UNINITIALIZED);
}
}

92
thirdparty/sdl/thread/SDL_thread_c.h vendored Normal file
View File

@@ -0,0 +1,92 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifndef SDL_thread_c_h_
#define SDL_thread_c_h_
// Need the definitions of SYS_ThreadHandle
#ifdef SDL_THREADS_DISABLED
#include "generic/SDL_systhread_c.h"
#elif defined(SDL_THREAD_PTHREAD)
#include "pthread/SDL_systhread_c.h"
#elif defined(SDL_THREAD_WINDOWS)
#include "windows/SDL_systhread_c.h"
#elif defined(SDL_THREAD_PS2)
#include "ps2/SDL_systhread_c.h"
#elif defined(SDL_THREAD_PSP)
#include "psp/SDL_systhread_c.h"
#elif defined(SDL_THREAD_VITA)
#include "vita/SDL_systhread_c.h"
#elif defined(SDL_THREAD_N3DS)
#include "n3ds/SDL_systhread_c.h"
#else
#error Need thread implementation for this platform
#include "generic/SDL_systhread_c.h"
#endif
#include "../SDL_error_c.h"
// This is the system-independent thread info structure
struct SDL_Thread
{
SDL_ThreadID threadid;
SYS_ThreadHandle handle;
int status;
SDL_AtomicInt state; /* SDL_ThreadState */
SDL_error errbuf;
char *name;
size_t stacksize; // 0 for default, >0 for user-specified stack size.
int(SDLCALL *userfunc)(void *);
void *userdata;
void *data;
SDL_FunctionPointer endfunc; // only used on some platforms.
};
// This is the function called to run a thread
extern void SDL_RunThread(SDL_Thread *thread);
// This is the system-independent thread local storage structure
typedef struct
{
int limit;
struct
{
void *data;
void(SDLCALL *destructor)(void *);
} array[1];
} SDL_TLSData;
// This is how many TLS entries we allocate at once
#define TLS_ALLOC_CHUNKSIZE 4
extern void SDL_InitTLSData(void);
extern void SDL_QuitTLSData(void);
/* Generic TLS support.
This is only intended as a fallback if getting real thread-local
storage fails or isn't supported on this platform.
*/
extern void SDL_Generic_InitTLSData(void);
extern SDL_TLSData *SDL_Generic_GetTLSData(void);
extern bool SDL_Generic_SetTLSData(SDL_TLSData *data);
extern void SDL_Generic_QuitTLSData(void);
#endif // SDL_thread_c_h_

View File

@@ -0,0 +1,216 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
// An implementation of condition variables using semaphores and mutexes
/*
This implementation borrows heavily from the BeOS condition variable
implementation, written by Christopher Tate and Owen Smith. Thanks!
*/
#include "../generic/SDL_syscond_c.h"
/* If two implementations are to be compiled into SDL (the active one
* will be chosen at runtime), the function names need to be
* suffixed
*/
#ifndef SDL_THREAD_GENERIC_COND_SUFFIX
#define SDL_CreateCondition_generic SDL_CreateCondition
#define SDL_DestroyCondition_generic SDL_DestroyCondition
#define SDL_SignalCondition_generic SDL_SignalCondition
#define SDL_BroadcastCondition_generic SDL_BroadcastCondition
#endif
typedef struct SDL_cond_generic
{
SDL_Semaphore *sem;
SDL_Semaphore *handshake_sem;
SDL_Semaphore *signal_sem;
int num_waiting;
int num_signals;
} SDL_cond_generic;
// Create a condition variable
SDL_Condition *SDL_CreateCondition_generic(void)
{
SDL_cond_generic *cond = (SDL_cond_generic *)SDL_calloc(1, sizeof(*cond));
#ifndef SDL_THREADS_DISABLED
if (cond) {
cond->sem = SDL_CreateSemaphore(0);
cond->handshake_sem = SDL_CreateSemaphore(0);
cond->signal_sem = SDL_CreateSemaphore(1);
if (!cond->sem || !cond->handshake_sem || !cond->signal_sem) {
SDL_DestroyCondition_generic((SDL_Condition *)cond);
cond = NULL;
}
}
#endif
return (SDL_Condition *)cond;
}
// Destroy a condition variable
void SDL_DestroyCondition_generic(SDL_Condition *_cond)
{
SDL_cond_generic *cond = (SDL_cond_generic *)_cond;
if (cond) {
if (cond->sem) {
SDL_DestroySemaphore(cond->sem);
}
if (cond->handshake_sem) {
SDL_DestroySemaphore(cond->handshake_sem);
}
if (cond->signal_sem) {
SDL_DestroySemaphore(cond->signal_sem);
}
SDL_free(cond);
}
}
// Restart one of the threads that are waiting on the condition variable
void SDL_SignalCondition_generic(SDL_Condition *_cond)
{
SDL_cond_generic *cond = (SDL_cond_generic *)_cond;
if (!cond) {
return;
}
#ifndef SDL_THREADS_DISABLED
/* If there are waiting threads not already signalled, then
signal the condition and wait for the thread to respond.
*/
SDL_WaitSemaphore(cond->signal_sem);
if (cond->num_waiting > cond->num_signals) {
cond->num_signals++;
SDL_SignalSemaphore(cond->sem);
SDL_SignalSemaphore(cond->signal_sem);
SDL_WaitSemaphore(cond->handshake_sem);
} else {
SDL_SignalSemaphore(cond->signal_sem);
}
#endif
}
// Restart all threads that are waiting on the condition variable
void SDL_BroadcastCondition_generic(SDL_Condition *_cond)
{
SDL_cond_generic *cond = (SDL_cond_generic *)_cond;
if (!cond) {
return;
}
#ifndef SDL_THREADS_DISABLED
/* If there are waiting threads not already signalled, then
signal the condition and wait for the thread to respond.
*/
SDL_WaitSemaphore(cond->signal_sem);
if (cond->num_waiting > cond->num_signals) {
const int num_waiting = (cond->num_waiting - cond->num_signals);
cond->num_signals = cond->num_waiting;
for (int i = 0; i < num_waiting; i++) {
SDL_SignalSemaphore(cond->sem);
}
/* Now all released threads are blocked here, waiting for us.
Collect them all (and win fabulous prizes!) :-)
*/
SDL_SignalSemaphore(cond->signal_sem);
for (int i = 0; i < num_waiting; i++) {
SDL_WaitSemaphore(cond->handshake_sem);
}
} else {
SDL_SignalSemaphore(cond->signal_sem);
}
#endif
}
/* Wait on the condition variable for at most 'timeoutNS' nanoseconds.
The mutex must be locked before entering this function!
The mutex is unlocked during the wait, and locked again after the wait.
Typical use:
Thread A:
SDL_LockMutex(lock);
while ( ! condition ) {
SDL_WaitCondition(cond, lock);
}
SDL_UnlockMutex(lock);
Thread B:
SDL_LockMutex(lock);
...
condition = true;
...
SDL_SignalCondition(cond);
SDL_UnlockMutex(lock);
*/
bool SDL_WaitConditionTimeoutNS_generic(SDL_Condition *_cond, SDL_Mutex *mutex, Sint64 timeoutNS)
{
SDL_cond_generic *cond = (SDL_cond_generic *)_cond;
bool result = true;
if (!cond || !mutex) {
return true;
}
#ifndef SDL_THREADS_DISABLED
/* Obtain the protection mutex, and increment the number of waiters.
This allows the signal mechanism to only perform a signal if there
are waiting threads.
*/
SDL_WaitSemaphore(cond->signal_sem);
cond->num_waiting++;
SDL_SignalSemaphore(cond->signal_sem);
// Unlock the mutex, as is required by condition variable semantics
SDL_UnlockMutex(mutex);
// Wait for a signal
result = SDL_WaitSemaphoreTimeoutNS(cond->sem, timeoutNS);
/* Let the signaler know we have completed the wait, otherwise
the signaler can race ahead and get the condition semaphore
if we are stopped between the mutex unlock and semaphore wait,
giving a deadlock. See the following URL for details:
http://web.archive.org/web/20010914175514/http://www-classic.be.com/aboutbe/benewsletter/volume_III/Issue40.html#Workshop
*/
SDL_WaitSemaphore(cond->signal_sem);
if (cond->num_signals > 0) {
SDL_SignalSemaphore(cond->handshake_sem);
cond->num_signals--;
}
cond->num_waiting--;
SDL_SignalSemaphore(cond->signal_sem);
// Lock the mutex, as is required by condition variable semantics
SDL_LockMutex(mutex);
#endif
return result;
}
#ifndef SDL_THREAD_GENERIC_COND_SUFFIX
bool SDL_WaitConditionTimeoutNS(SDL_Condition *cond, SDL_Mutex *mutex, Sint64 timeoutNS)
{
return SDL_WaitConditionTimeoutNS_generic(cond, mutex, timeoutNS);
}
#endif

View File

@@ -0,0 +1,36 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifndef SDL_syscond_generic_h_
#define SDL_syscond_generic_h_
#ifdef SDL_THREAD_GENERIC_COND_SUFFIX
SDL_Condition *SDL_CreateCondition_generic(void);
void SDL_DestroyCondition_generic(SDL_Condition *cond);
void SDL_SignalCondition_generic(SDL_Condition *cond);
void SDL_BroadcastCondition_generic(SDL_Condition *cond);
bool SDL_WaitConditionTimeoutNS_generic(SDL_Condition *cond, SDL_Mutex *mutex, Sint64 timeoutNS);
#endif // SDL_THREAD_GENERIC_COND_SUFFIX
#endif // SDL_syscond_generic_h_

View File

@@ -0,0 +1,194 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
// An implementation of rwlocks using mutexes, condition variables, and atomics.
#include "SDL_systhread_c.h"
#include "../generic/SDL_sysrwlock_c.h"
/* If two implementations are to be compiled into SDL (the active one
* will be chosen at runtime), the function names need to be
* suffixed
*/
// !!! FIXME: this is quite a tapdance with macros and the build system, maybe we can simplify how we do this. --ryan.
#ifndef SDL_THREAD_GENERIC_RWLOCK_SUFFIX
#define SDL_CreateRWLock_generic SDL_CreateRWLock
#define SDL_DestroyRWLock_generic SDL_DestroyRWLock
#define SDL_LockRWLockForReading_generic SDL_LockRWLockForReading
#define SDL_LockRWLockForWriting_generic SDL_LockRWLockForWriting
#define SDL_UnlockRWLock_generic SDL_UnlockRWLock
#endif
struct SDL_RWLock
{
#ifdef SDL_THREADS_DISABLED
int unused;
#else
SDL_Mutex *lock;
SDL_Condition *condition;
SDL_ThreadID writer_thread;
SDL_AtomicInt reader_count;
SDL_AtomicInt writer_count;
#endif
};
SDL_RWLock *SDL_CreateRWLock_generic(void)
{
SDL_RWLock *rwlock = (SDL_RWLock *) SDL_calloc(1, sizeof (*rwlock));
if (!rwlock) {
return NULL;
}
#ifndef SDL_THREADS_DISABLED
rwlock->lock = SDL_CreateMutex();
if (!rwlock->lock) {
SDL_free(rwlock);
return NULL;
}
rwlock->condition = SDL_CreateCondition();
if (!rwlock->condition) {
SDL_DestroyMutex(rwlock->lock);
SDL_free(rwlock);
return NULL;
}
SDL_SetAtomicInt(&rwlock->reader_count, 0);
SDL_SetAtomicInt(&rwlock->writer_count, 0);
#endif
return rwlock;
}
void SDL_DestroyRWLock_generic(SDL_RWLock *rwlock)
{
if (rwlock) {
#ifndef SDL_THREADS_DISABLED
SDL_DestroyMutex(rwlock->lock);
SDL_DestroyCondition(rwlock->condition);
#endif
SDL_free(rwlock);
}
}
void SDL_LockRWLockForReading_generic(SDL_RWLock *rwlock) SDL_NO_THREAD_SAFETY_ANALYSIS // clang doesn't know about NULL mutexes
{
#ifndef SDL_THREADS_DISABLED
if (rwlock) {
// !!! FIXME: these don't have to be atomic, we always gate them behind a mutex.
SDL_LockMutex(rwlock->lock);
SDL_assert(SDL_GetAtomicInt(&rwlock->writer_count) == 0); // shouldn't be able to grab lock if there's a writer!
SDL_AddAtomicInt(&rwlock->reader_count, 1);
SDL_UnlockMutex(rwlock->lock); // other readers can attempt to share the lock.
}
#endif
}
void SDL_LockRWLockForWriting_generic(SDL_RWLock *rwlock) SDL_NO_THREAD_SAFETY_ANALYSIS // clang doesn't know about NULL mutexes
{
#ifndef SDL_THREADS_DISABLED
if (rwlock) {
SDL_LockMutex(rwlock->lock);
while (SDL_GetAtomicInt(&rwlock->reader_count) > 0) { // while something is holding the shared lock, keep waiting.
SDL_WaitCondition(rwlock->condition, rwlock->lock); // release the lock and wait for readers holding the shared lock to release it, regrab the lock.
}
// we hold the lock!
SDL_AddAtomicInt(&rwlock->writer_count, 1); // we let these be recursive, but the API doesn't require this. It _does_ trust you unlock correctly!
}
#endif
}
bool SDL_TryLockRWLockForReading_generic(SDL_RWLock *rwlock)
{
#ifndef SDL_THREADS_DISABLED
if (rwlock) {
if (!SDL_TryLockMutex(rwlock->lock)) {
// !!! FIXME: there is a small window where a reader has to lock the mutex, and if we hit that, we will return SDL_RWLOCK_TIMEDOUT even though we could have shared the lock.
return false;
}
SDL_assert(SDL_GetAtomicInt(&rwlock->writer_count) == 0); // shouldn't be able to grab lock if there's a writer!
SDL_AddAtomicInt(&rwlock->reader_count, 1);
SDL_UnlockMutex(rwlock->lock); // other readers can attempt to share the lock.
}
#endif
return true;
}
#ifndef SDL_THREAD_GENERIC_RWLOCK_SUFFIX
bool SDL_TryLockRWLockForReading(SDL_RWLock *rwlock)
{
return SDL_TryLockRWLockForReading_generic(rwlock);
}
#endif
bool SDL_TryLockRWLockForWriting_generic(SDL_RWLock *rwlock)
{
#ifndef SDL_THREADS_DISABLED
if (rwlock) {
if (!SDL_TryLockMutex(rwlock->lock)) {
return false;
}
if (SDL_GetAtomicInt(&rwlock->reader_count) > 0) { // a reader is using the shared lock, treat it as unavailable.
SDL_UnlockMutex(rwlock->lock);
return false;
}
// we hold the lock!
SDL_AddAtomicInt(&rwlock->writer_count, 1); // we let these be recursive, but the API doesn't require this. It _does_ trust you unlock correctly!
}
#endif
return true;
}
#ifndef SDL_THREAD_GENERIC_RWLOCK_SUFFIX
bool SDL_TryLockRWLockForWriting(SDL_RWLock *rwlock)
{
return SDL_TryLockRWLockForWriting_generic(rwlock);
}
#endif
void SDL_UnlockRWLock_generic(SDL_RWLock *rwlock) SDL_NO_THREAD_SAFETY_ANALYSIS // clang doesn't know about NULL mutexes
{
#ifndef SDL_THREADS_DISABLED
if (rwlock) {
SDL_LockMutex(rwlock->lock); // recursive lock for writers, readers grab lock to make sure things are sane.
if (SDL_GetAtomicInt(&rwlock->reader_count) > 0) { // we're a reader
SDL_AddAtomicInt(&rwlock->reader_count, -1);
SDL_BroadcastCondition(rwlock->condition); // alert any pending writers to attempt to try to grab the lock again.
} else if (SDL_GetAtomicInt(&rwlock->writer_count) > 0) { // we're a writer
SDL_AddAtomicInt(&rwlock->writer_count, -1);
SDL_UnlockMutex(rwlock->lock); // recursive unlock.
}
SDL_UnlockMutex(rwlock->lock);
}
#endif
}

View File

@@ -0,0 +1,38 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifndef SDL_sysrwlock_c_h_
#define SDL_sysrwlock_c_h_
#ifdef SDL_THREAD_GENERIC_RWLOCK_SUFFIX
SDL_RWLock *SDL_CreateRWLock_generic(void);
void SDL_DestroyRWLock_generic(SDL_RWLock *rwlock);
void SDL_LockRWLockForReading_generic(SDL_RWLock *rwlock);
void SDL_LockRWLockForWriting_generic(SDL_RWLock *rwlock);
bool SDL_TryLockRWLockForReading_generic(SDL_RWLock *rwlock);
bool SDL_TryLockRWLockForWriting_generic(SDL_RWLock *rwlock);
void SDL_UnlockRWLock_generic(SDL_RWLock *rwlock);
#endif // SDL_THREAD_GENERIC_RWLOCK_SUFFIX
#endif // SDL_sysrwlock_c_h_

View File

@@ -0,0 +1,183 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
// An implementation of semaphores using mutexes and condition variables
#include "SDL_systhread_c.h"
#ifdef SDL_THREADS_DISABLED
SDL_Semaphore *SDL_CreateSemaphore(Uint32 initial_value)
{
SDL_SetError("SDL not built with thread support");
return NULL;
}
void SDL_DestroySemaphore(SDL_Semaphore *sem)
{
}
bool SDL_WaitSemaphoreTimeoutNS(SDL_Semaphore *sem, Sint64 timeoutNS)
{
return true;
}
Uint32 SDL_GetSemaphoreValue(SDL_Semaphore *sem)
{
return 0;
}
void SDL_SignalSemaphore(SDL_Semaphore *sem)
{
return;
}
#else
struct SDL_Semaphore
{
Uint32 count;
Uint32 waiters_count;
SDL_Mutex *count_lock;
SDL_Condition *count_nonzero;
};
SDL_Semaphore *SDL_CreateSemaphore(Uint32 initial_value)
{
SDL_Semaphore *sem;
sem = (SDL_Semaphore *)SDL_malloc(sizeof(*sem));
if (!sem) {
return NULL;
}
sem->count = initial_value;
sem->waiters_count = 0;
sem->count_lock = SDL_CreateMutex();
sem->count_nonzero = SDL_CreateCondition();
if (!sem->count_lock || !sem->count_nonzero) {
SDL_DestroySemaphore(sem);
return NULL;
}
return sem;
}
/* WARNING:
You cannot call this function when another thread is using the semaphore.
*/
void SDL_DestroySemaphore(SDL_Semaphore *sem)
{
if (sem) {
sem->count = 0xFFFFFFFF;
while (sem->waiters_count > 0) {
SDL_SignalCondition(sem->count_nonzero);
SDL_Delay(10);
}
SDL_DestroyCondition(sem->count_nonzero);
if (sem->count_lock) {
SDL_LockMutex(sem->count_lock);
SDL_UnlockMutex(sem->count_lock);
SDL_DestroyMutex(sem->count_lock);
}
SDL_free(sem);
}
}
bool SDL_WaitSemaphoreTimeoutNS(SDL_Semaphore *sem, Sint64 timeoutNS)
{
bool result = false;
if (!sem) {
return true;
}
if (timeoutNS == 0) {
SDL_LockMutex(sem->count_lock);
if (sem->count > 0) {
--sem->count;
result = true;
}
SDL_UnlockMutex(sem->count_lock);
} else if (timeoutNS < 0) {
SDL_LockMutex(sem->count_lock);
++sem->waiters_count;
while (sem->count == 0) {
SDL_WaitConditionTimeoutNS(sem->count_nonzero, sem->count_lock, -1);
}
--sem->waiters_count;
--sem->count;
result = true;
SDL_UnlockMutex(sem->count_lock);
} else {
Uint64 stop_time = SDL_GetTicksNS() + timeoutNS;
SDL_LockMutex(sem->count_lock);
++sem->waiters_count;
while (sem->count == 0) {
Sint64 waitNS = (Sint64)(stop_time - SDL_GetTicksNS());
if (waitNS > 0) {
SDL_WaitConditionTimeoutNS(sem->count_nonzero, sem->count_lock, waitNS);
} else {
break;
}
}
--sem->waiters_count;
if (sem->count > 0) {
--sem->count;
result = true;
}
SDL_UnlockMutex(sem->count_lock);
}
return result;
}
Uint32 SDL_GetSemaphoreValue(SDL_Semaphore *sem)
{
Uint32 value;
value = 0;
if (sem) {
SDL_LockMutex(sem->count_lock);
value = sem->count;
SDL_UnlockMutex(sem->count_lock);
}
return value;
}
void SDL_SignalSemaphore(SDL_Semaphore *sem)
{
if (!sem) {
return;
}
SDL_LockMutex(sem->count_lock);
if (sem->waiters_count > 0) {
SDL_SignalCondition(sem->count_nonzero);
}
++sem->count;
SDL_UnlockMutex(sem->count_lock);
}
#endif // SDL_THREADS_DISABLED

View File

@@ -0,0 +1,24 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
// Stub until we implement threads on this platform
typedef int SYS_ThreadHandle;

View File

@@ -0,0 +1,128 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#include <sys/time.h>
#include <time.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#include "SDL_sysmutex_c.h"
struct SDL_Condition
{
pthread_cond_t cond;
};
// Create a condition variable
SDL_Condition *SDL_CreateCondition(void)
{
SDL_Condition *cond;
cond = (SDL_Condition *)SDL_malloc(sizeof(SDL_Condition));
if (cond) {
if (pthread_cond_init(&cond->cond, NULL) != 0) {
SDL_SetError("pthread_cond_init() failed");
SDL_free(cond);
cond = NULL;
}
}
return cond;
}
// Destroy a condition variable
void SDL_DestroyCondition(SDL_Condition *cond)
{
if (cond) {
pthread_cond_destroy(&cond->cond);
SDL_free(cond);
}
}
// Restart one of the threads that are waiting on the condition variable
void SDL_SignalCondition(SDL_Condition *cond)
{
if (!cond) {
return;
}
pthread_cond_signal(&cond->cond);
}
// Restart all threads that are waiting on the condition variable
void SDL_BroadcastCondition(SDL_Condition *cond)
{
if (!cond) {
return;
}
pthread_cond_broadcast(&cond->cond);
}
bool SDL_WaitConditionTimeoutNS(SDL_Condition *cond, SDL_Mutex *mutex, Sint64 timeoutNS)
{
#ifndef HAVE_CLOCK_GETTIME
struct timeval delta;
#endif
struct timespec abstime;
if (!cond || !mutex) {
return true;
}
if (timeoutNS < 0) {
return (pthread_cond_wait(&cond->cond, &mutex->id) == 0);
}
#ifdef HAVE_CLOCK_GETTIME
clock_gettime(CLOCK_REALTIME, &abstime);
abstime.tv_sec += (timeoutNS / SDL_NS_PER_SECOND);
abstime.tv_nsec += (timeoutNS % SDL_NS_PER_SECOND);
#else
gettimeofday(&delta, NULL);
abstime.tv_sec = delta.tv_sec + (timeoutNS / SDL_NS_PER_SECOND);
abstime.tv_nsec = SDL_US_TO_NS(delta.tv_usec) + (timeoutNS % SDL_NS_PER_SECOND);
#endif
while (abstime.tv_nsec >= 1000000000) {
abstime.tv_sec += 1;
abstime.tv_nsec -= 1000000000;
}
bool result;
int rc;
tryagain:
rc = pthread_cond_timedwait(&cond->cond, &mutex->id, &abstime);
switch (rc) {
case EINTR:
goto tryagain;
// break; -Wunreachable-code-break
case ETIMEDOUT:
result = false;
break;
default:
result = true;
break;
}
return result;
}

View File

@@ -0,0 +1,154 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#include <errno.h>
#include <pthread.h>
#include "SDL_sysmutex_c.h"
SDL_Mutex *SDL_CreateMutex(void)
{
SDL_Mutex *mutex;
pthread_mutexattr_t attr;
// Allocate the structure
mutex = (SDL_Mutex *)SDL_calloc(1, sizeof(*mutex));
if (mutex) {
pthread_mutexattr_init(&attr);
#ifdef SDL_THREAD_PTHREAD_RECURSIVE_MUTEX
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
#elif defined(SDL_THREAD_PTHREAD_RECURSIVE_MUTEX_NP)
pthread_mutexattr_setkind_np(&attr, PTHREAD_MUTEX_RECURSIVE_NP);
#else
// No extra attributes necessary
#endif
if (pthread_mutex_init(&mutex->id, &attr) != 0) {
SDL_SetError("pthread_mutex_init() failed");
SDL_free(mutex);
mutex = NULL;
}
}
return mutex;
}
void SDL_DestroyMutex(SDL_Mutex *mutex)
{
if (mutex) {
pthread_mutex_destroy(&mutex->id);
SDL_free(mutex);
}
}
void SDL_LockMutex(SDL_Mutex *mutex) SDL_NO_THREAD_SAFETY_ANALYSIS // clang doesn't know about NULL mutexes
{
if (mutex) {
#ifdef FAKE_RECURSIVE_MUTEX
pthread_t this_thread = pthread_self();
if (mutex->owner == this_thread) {
++mutex->recursive;
} else {
/* The order of operations is important.
We set the locking thread id after we obtain the lock
so unlocks from other threads will fail.
*/
const int rc = pthread_mutex_lock(&mutex->id);
SDL_assert(rc == 0); // assume we're in a lot of trouble if this assert fails.
mutex->owner = this_thread;
mutex->recursive = 0;
}
#else
const int rc = pthread_mutex_lock(&mutex->id);
SDL_assert(rc == 0); // assume we're in a lot of trouble if this assert fails.
#endif
}
}
bool SDL_TryLockMutex(SDL_Mutex *mutex)
{
bool result = true;
if (mutex) {
#ifdef FAKE_RECURSIVE_MUTEX
pthread_t this_thread = pthread_self();
if (mutex->owner == this_thread) {
++mutex->recursive;
} else {
/* The order of operations is important.
We set the locking thread id after we obtain the lock
so unlocks from other threads will fail.
*/
const int rc = pthread_mutex_trylock(&mutex->id);
if (rc == 0) {
mutex->owner = this_thread;
mutex->recursive = 0;
} else if (rc == EBUSY) {
result = false;
} else {
SDL_assert(!"Error trying to lock mutex"); // assume we're in a lot of trouble if this assert fails.
result = false;
}
}
#else
const int rc = pthread_mutex_trylock(&mutex->id);
if (rc != 0) {
if (rc == EBUSY) {
result = false;
} else {
SDL_assert(!"Error trying to lock mutex"); // assume we're in a lot of trouble if this assert fails.
result = false;
}
}
#endif
}
return result;
}
void SDL_UnlockMutex(SDL_Mutex *mutex) SDL_NO_THREAD_SAFETY_ANALYSIS // clang doesn't know about NULL mutexes
{
if (mutex) {
#ifdef FAKE_RECURSIVE_MUTEX
// We can only unlock the mutex if we own it
if (pthread_self() == mutex->owner) {
if (mutex->recursive) {
--mutex->recursive;
} else {
/* The order of operations is important.
First reset the owner so another thread doesn't lock
the mutex and set the ownership before we reset it,
then release the lock semaphore.
*/
mutex->owner = 0;
pthread_mutex_unlock(&mutex->id);
}
} else {
SDL_SetError("mutex not owned by this thread");
return;
}
#else
const int rc = pthread_mutex_unlock(&mutex->id);
SDL_assert(rc == 0); // assume we're in a lot of trouble if this assert fails.
#endif // FAKE_RECURSIVE_MUTEX
}
}

View File

@@ -0,0 +1,40 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifndef SDL_mutex_c_h_
#define SDL_mutex_c_h_
#if !(defined(SDL_THREAD_PTHREAD_RECURSIVE_MUTEX) || \
defined(SDL_THREAD_PTHREAD_RECURSIVE_MUTEX_NP))
#define FAKE_RECURSIVE_MUTEX
#endif
struct SDL_Mutex
{
pthread_mutex_t id;
#ifdef FAKE_RECURSIVE_MUTEX
int recursive;
pthread_t owner;
#endif
};
#endif // SDL_mutex_c_h_

View File

@@ -0,0 +1,113 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#include <errno.h>
#include <pthread.h>
struct SDL_RWLock
{
pthread_rwlock_t id;
};
SDL_RWLock *SDL_CreateRWLock(void)
{
SDL_RWLock *rwlock;
// Allocate the structure
rwlock = (SDL_RWLock *)SDL_calloc(1, sizeof(*rwlock));
if (rwlock) {
if (pthread_rwlock_init(&rwlock->id, NULL) != 0) {
SDL_SetError("pthread_rwlock_init() failed");
SDL_free(rwlock);
rwlock = NULL;
}
}
return rwlock;
}
void SDL_DestroyRWLock(SDL_RWLock *rwlock)
{
if (rwlock) {
pthread_rwlock_destroy(&rwlock->id);
SDL_free(rwlock);
}
}
void SDL_LockRWLockForReading(SDL_RWLock *rwlock) SDL_NO_THREAD_SAFETY_ANALYSIS // clang doesn't know about NULL mutexes
{
if (rwlock) {
const int rc = pthread_rwlock_rdlock(&rwlock->id);
SDL_assert(rc == 0); // assume we're in a lot of trouble if this assert fails.
}
}
void SDL_LockRWLockForWriting(SDL_RWLock *rwlock) SDL_NO_THREAD_SAFETY_ANALYSIS // clang doesn't know about NULL mutexes
{
if (rwlock) {
const int rc = pthread_rwlock_wrlock(&rwlock->id);
SDL_assert(rc == 0); // assume we're in a lot of trouble if this assert fails.
}
}
bool SDL_TryLockRWLockForReading(SDL_RWLock *rwlock)
{
bool result = true;
if (rwlock) {
const int rc = pthread_rwlock_tryrdlock(&rwlock->id);
if (rc != 0) {
result = false;
if (rc != EBUSY) {
SDL_assert(!"Error trying to lock rwlock for reading"); // assume we're in a lot of trouble if this assert fails.
}
}
}
return result;
}
bool SDL_TryLockRWLockForWriting(SDL_RWLock *rwlock)
{
bool result = true;
if (rwlock) {
const int rc = pthread_rwlock_trywrlock(&rwlock->id);
if (rc != 0) {
result = false;
if (rc != EBUSY) {
SDL_assert(!"Error trying to lock rwlock for writing"); // assume we're in a lot of trouble if this assert fails.
}
}
}
return result;
}
void SDL_UnlockRWLock(SDL_RWLock *rwlock) SDL_NO_THREAD_SAFETY_ANALYSIS // clang doesn't know about NULL mutexes
{
if (rwlock) {
const int rc = pthread_rwlock_unlock(&rwlock->id);
SDL_assert(rc == 0); // assume we're in a lot of trouble if this assert fails.
}
}

View File

@@ -0,0 +1,160 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#include <errno.h>
#include <pthread.h>
#include <semaphore.h>
#include <sys/time.h>
#include <time.h>
// Wrapper around POSIX 1003.1b semaphores
#if defined(SDL_PLATFORM_MACOS) || defined(SDL_PLATFORM_IOS)
// macOS doesn't support sem_getvalue() as of version 10.4
#include "../generic/SDL_syssem.c"
#else
struct SDL_Semaphore
{
sem_t sem;
};
// Create a semaphore, initialized with value
SDL_Semaphore *SDL_CreateSemaphore(Uint32 initial_value)
{
SDL_Semaphore *sem = (SDL_Semaphore *)SDL_malloc(sizeof(SDL_Semaphore));
if (sem) {
if (sem_init(&sem->sem, 0, initial_value) < 0) {
SDL_SetError("sem_init() failed");
SDL_free(sem);
sem = NULL;
}
}
return sem;
}
void SDL_DestroySemaphore(SDL_Semaphore *sem)
{
if (sem) {
sem_destroy(&sem->sem);
SDL_free(sem);
}
}
bool SDL_WaitSemaphoreTimeoutNS(SDL_Semaphore *sem, Sint64 timeoutNS)
{
#ifdef HAVE_SEM_TIMEDWAIT
#ifndef HAVE_CLOCK_GETTIME
struct timeval now;
#endif
struct timespec ts_timeout;
#else
Uint64 stop_time;
#endif
if (!sem) {
return true;
}
// Try the easy cases first
if (timeoutNS == 0) {
return (sem_trywait(&sem->sem) == 0);
}
if (timeoutNS < 0) {
int rc;
do {
rc = sem_wait(&sem->sem);
} while (rc < 0 && errno == EINTR);
return (rc == 0);
}
#ifdef HAVE_SEM_TIMEDWAIT
/* Setup the timeout. sem_timedwait doesn't wait for
* a lapse of time, but until we reach a certain time.
* This time is now plus the timeout.
*/
#ifdef HAVE_CLOCK_GETTIME
clock_gettime(CLOCK_REALTIME, &ts_timeout);
// Add our timeout to current time
ts_timeout.tv_sec += (timeoutNS / SDL_NS_PER_SECOND);
ts_timeout.tv_nsec += (timeoutNS % SDL_NS_PER_SECOND);
#else
gettimeofday(&now, NULL);
// Add our timeout to current time
ts_timeout.tv_sec = now.tv_sec + (timeoutNS / SDL_NS_PER_SECOND);
ts_timeout.tv_nsec = SDL_US_TO_NS(now.tv_usec) + (timeoutNS % SDL_NS_PER_SECOND);
#endif
// Wrap the second if needed
while (ts_timeout.tv_nsec >= 1000000000) {
ts_timeout.tv_sec += 1;
ts_timeout.tv_nsec -= 1000000000;
}
// Wait.
int rc;
do {
rc = sem_timedwait(&sem->sem, &ts_timeout);
} while (rc < 0 && errno == EINTR);
return (rc == 0);
#else
stop_time = SDL_GetTicksNS() + timeoutNS;
while (sem_trywait(&sem->sem) != 0) {
if (SDL_GetTicksNS() >= stop_time) {
return false;
}
SDL_DelayNS(100);
}
return true;
#endif // HAVE_SEM_TIMEDWAIT
}
Uint32 SDL_GetSemaphoreValue(SDL_Semaphore *sem)
{
int ret = 0;
if (!sem) {
return 0;
}
sem_getvalue(&sem->sem, &ret);
if (ret < 0) {
ret = 0;
}
return (Uint32)ret;
}
void SDL_SignalSemaphore(SDL_Semaphore *sem)
{
if (!sem) {
return;
}
sem_post(&sem->sem);
}
#endif // SDL_PLATFORM_MACOS

View File

@@ -0,0 +1,293 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#include <pthread.h>
#ifdef HAVE_PTHREAD_NP_H
#include <pthread_np.h>
#endif
#ifdef HAVE_SIGNAL_H
#include <signal.h>
#endif
#include <errno.h>
#ifdef SDL_PLATFORM_LINUX
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/syscall.h>
#include <unistd.h>
#include "../../core/linux/SDL_dbus.h"
#endif // SDL_PLATFORM_LINUX
#if (defined(SDL_PLATFORM_LINUX) || defined(SDL_PLATFORM_ANDROID) || defined(SDL_PLATFORM_MACOS) || defined(SDL_PLATFORM_IOS)) && defined(HAVE_DLOPEN)
#include <dlfcn.h>
#ifndef RTLD_DEFAULT
#define RTLD_DEFAULT NULL
#endif
#endif
#include "../SDL_thread_c.h"
#include "../SDL_systhread.h"
#ifdef SDL_PLATFORM_ANDROID
#include "../../core/android/SDL_android.h"
#endif
#ifdef SDL_PLATFORM_HAIKU
#include <kernel/OS.h>
#endif
#ifdef HAVE_SIGNAL_H
// List of signals to mask in the subthreads
static const int sig_list[] = {
SIGHUP, SIGINT, SIGQUIT, SIGPIPE, SIGALRM, SIGTERM, SIGCHLD, SIGWINCH,
SIGVTALRM, SIGPROF, 0
};
#endif
static void *RunThread(void *data)
{
#ifdef SDL_PLATFORM_ANDROID
Android_JNI_SetupThread();
#endif
SDL_RunThread((SDL_Thread *)data);
return NULL;
}
#if (defined(SDL_PLATFORM_MACOS) || defined(SDL_PLATFORM_IOS)) && defined(HAVE_DLOPEN)
static bool checked_setname = false;
static int (*ppthread_setname_np)(const char *) = NULL;
#elif (defined(SDL_PLATFORM_LINUX) || defined(SDL_PLATFORM_ANDROID)) && defined(HAVE_DLOPEN)
static bool checked_setname = false;
static int (*ppthread_setname_np)(pthread_t, const char *) = NULL;
#endif
bool SDL_SYS_CreateThread(SDL_Thread *thread,
SDL_FunctionPointer pfnBeginThread,
SDL_FunctionPointer pfnEndThread)
{
pthread_attr_t type;
// do this here before any threads exist, so there's no race condition.
#if (defined(SDL_PLATFORM_MACOS) || defined(SDL_PLATFORM_IOS) || defined(SDL_PLATFORM_LINUX) || defined(SDL_PLATFORM_ANDROID)) && defined(HAVE_DLOPEN)
if (!checked_setname) {
void *fn = dlsym(RTLD_DEFAULT, "pthread_setname_np");
#if defined(SDL_PLATFORM_MACOS) || defined(SDL_PLATFORM_IOS)
ppthread_setname_np = (int (*)(const char *))fn;
#elif defined(SDL_PLATFORM_LINUX) || defined(SDL_PLATFORM_ANDROID)
ppthread_setname_np = (int (*)(pthread_t, const char *))fn;
#endif
checked_setname = true;
}
#endif
// Set the thread attributes
if (pthread_attr_init(&type) != 0) {
return SDL_SetError("Couldn't initialize pthread attributes");
}
pthread_attr_setdetachstate(&type, PTHREAD_CREATE_JOINABLE);
// Set caller-requested stack size. Otherwise: use the system default.
if (thread->stacksize) {
pthread_attr_setstacksize(&type, thread->stacksize);
}
// Create the thread and go!
if (pthread_create(&thread->handle, &type, RunThread, thread) != 0) {
return SDL_SetError("Not enough resources to create thread");
}
return true;
}
void SDL_SYS_SetupThread(const char *name)
{
#ifdef HAVE_SIGNAL_H
int i;
sigset_t mask;
#endif
if (name) {
#if (defined(SDL_PLATFORM_MACOS) || defined(SDL_PLATFORM_IOS) || defined(SDL_PLATFORM_LINUX) || defined(SDL_PLATFORM_ANDROID)) && defined(HAVE_DLOPEN)
SDL_assert(checked_setname);
if (ppthread_setname_np) {
#if defined(SDL_PLATFORM_MACOS) || defined(SDL_PLATFORM_IOS)
ppthread_setname_np(name);
#elif defined(SDL_PLATFORM_LINUX) || defined(SDL_PLATFORM_ANDROID)
if (ppthread_setname_np(pthread_self(), name) == ERANGE) {
char namebuf[16]; // Limited to 16 char
SDL_strlcpy(namebuf, name, sizeof(namebuf));
ppthread_setname_np(pthread_self(), namebuf);
}
#endif
}
#elif defined(HAVE_PTHREAD_SETNAME_NP)
#ifdef SDL_PLATFORM_NETBSD
pthread_setname_np(pthread_self(), "%s", name);
#else
if (pthread_setname_np(pthread_self(), name) == ERANGE) {
char namebuf[16]; // Limited to 16 char
SDL_strlcpy(namebuf, name, sizeof(namebuf));
pthread_setname_np(pthread_self(), namebuf);
}
#endif
#elif defined(HAVE_PTHREAD_SET_NAME_NP)
pthread_set_name_np(pthread_self(), name);
#elif defined(SDL_PLATFORM_HAIKU)
// The docs say the thread name can't be longer than B_OS_NAME_LENGTH.
char namebuf[B_OS_NAME_LENGTH];
SDL_strlcpy(namebuf, name, sizeof(namebuf));
rename_thread(find_thread(NULL), namebuf);
#endif
}
#ifdef HAVE_SIGNAL_H
// Mask asynchronous signals for this thread
sigemptyset(&mask);
for (i = 0; sig_list[i]; ++i) {
sigaddset(&mask, sig_list[i]);
}
pthread_sigmask(SIG_BLOCK, &mask, 0);
#endif
#ifdef PTHREAD_CANCEL_ASYNCHRONOUS
// Allow ourselves to be asynchronously cancelled
{
int oldstate;
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldstate);
}
#endif
}
SDL_ThreadID SDL_GetCurrentThreadID(void)
{
return (SDL_ThreadID)pthread_self();
}
bool SDL_SYS_SetThreadPriority(SDL_ThreadPriority priority)
{
#ifdef SDL_PLATFORM_RISCOS
// FIXME: Setting thread priority does not seem to be supported
return true;
#else
struct sched_param sched;
int policy;
int pri_policy;
pthread_t thread = pthread_self();
const char *policyhint = SDL_GetHint(SDL_HINT_THREAD_PRIORITY_POLICY);
const bool timecritical_realtime_hint = SDL_GetHintBoolean(SDL_HINT_THREAD_FORCE_REALTIME_TIME_CRITICAL, false);
if (pthread_getschedparam(thread, &policy, &sched) != 0) {
return SDL_SetError("pthread_getschedparam() failed");
}
/* Higher priority levels may require changing the pthread scheduler policy
* for the thread. SDL will make such changes by default but there is
* also a hint allowing that behavior to be overridden. */
switch (priority) {
case SDL_THREAD_PRIORITY_LOW:
case SDL_THREAD_PRIORITY_NORMAL:
pri_policy = SCHED_OTHER;
break;
case SDL_THREAD_PRIORITY_HIGH:
case SDL_THREAD_PRIORITY_TIME_CRITICAL:
#if defined(SDL_PLATFORM_MACOS) || defined(SDL_PLATFORM_IOS) || defined(SDL_PLATFORM_TVOS)
// Apple requires SCHED_RR for high priority threads
pri_policy = SCHED_RR;
break;
#else
pri_policy = SCHED_OTHER;
break;
#endif
default:
pri_policy = policy;
break;
}
if (timecritical_realtime_hint && priority == SDL_THREAD_PRIORITY_TIME_CRITICAL) {
pri_policy = SCHED_RR;
}
if (policyhint) {
if (SDL_strcmp(policyhint, "current") == 0) {
// Leave current thread scheduler policy unchanged
} else if (SDL_strcmp(policyhint, "other") == 0) {
policy = SCHED_OTHER;
} else if (SDL_strcmp(policyhint, "rr") == 0) {
policy = SCHED_RR;
} else if (SDL_strcmp(policyhint, "fifo") == 0) {
policy = SCHED_FIFO;
} else {
policy = pri_policy;
}
} else {
policy = pri_policy;
}
#ifdef SDL_PLATFORM_LINUX
{
pid_t linuxTid = syscall(SYS_gettid);
return SDL_SetLinuxThreadPriorityAndPolicy(linuxTid, priority, policy);
}
#else
if (priority == SDL_THREAD_PRIORITY_LOW) {
sched.sched_priority = sched_get_priority_min(policy);
} else if (priority == SDL_THREAD_PRIORITY_TIME_CRITICAL) {
sched.sched_priority = sched_get_priority_max(policy);
} else {
int min_priority = sched_get_priority_min(policy);
int max_priority = sched_get_priority_max(policy);
#if defined(SDL_PLATFORM_MACOS) || defined(SDL_PLATFORM_IOS) || defined(SDL_PLATFORM_TVOS)
if (min_priority == 15 && max_priority == 47) {
// Apple has a specific set of thread priorities
if (priority == SDL_THREAD_PRIORITY_HIGH) {
sched.sched_priority = 45;
} else {
sched.sched_priority = 37;
}
} else
#endif // SDL_PLATFORM_MACOS || SDL_PLATFORM_IOS || SDL_PLATFORM_TVOS
{
sched.sched_priority = (min_priority + (max_priority - min_priority) / 2);
if (priority == SDL_THREAD_PRIORITY_HIGH) {
sched.sched_priority += ((max_priority - min_priority) / 4);
}
}
}
if (pthread_setschedparam(thread, policy, &sched) != 0) {
return SDL_SetError("pthread_setschedparam() failed");
}
return true;
#endif // linux
#endif // #if SDL_PLATFORM_RISCOS
}
void SDL_SYS_WaitThread(SDL_Thread *thread)
{
pthread_join(thread->handle, 0);
}
void SDL_SYS_DetachThread(SDL_Thread *thread)
{
pthread_detach(thread->handle);
}

View File

@@ -0,0 +1,25 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#include <pthread.h>
typedef pthread_t SYS_ThreadHandle;

View File

@@ -0,0 +1,78 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#include "../SDL_systhread.h"
#include "../SDL_thread_c.h"
#include <pthread.h>
#define INVALID_PTHREAD_KEY ((pthread_key_t)-1)
static pthread_key_t thread_local_storage = INVALID_PTHREAD_KEY;
static bool generic_local_storage = false;
void SDL_SYS_InitTLSData(void)
{
if (thread_local_storage == INVALID_PTHREAD_KEY && !generic_local_storage) {
if (pthread_key_create(&thread_local_storage, NULL) != 0) {
thread_local_storage = INVALID_PTHREAD_KEY;
SDL_Generic_InitTLSData();
generic_local_storage = true;
}
}
}
SDL_TLSData *SDL_SYS_GetTLSData(void)
{
if (generic_local_storage) {
return SDL_Generic_GetTLSData();
}
if (thread_local_storage != INVALID_PTHREAD_KEY) {
return (SDL_TLSData *)pthread_getspecific(thread_local_storage);
}
return NULL;
}
bool SDL_SYS_SetTLSData(SDL_TLSData *data)
{
if (generic_local_storage) {
return SDL_Generic_SetTLSData(data);
}
if (pthread_setspecific(thread_local_storage, data) != 0) {
return SDL_SetError("pthread_setspecific() failed");
}
return true;
}
void SDL_SYS_QuitTLSData(void)
{
if (generic_local_storage) {
SDL_Generic_QuitTLSData();
generic_local_storage = false;
} else {
if (thread_local_storage != INVALID_PTHREAD_KEY) {
pthread_key_delete(thread_local_storage);
thread_local_storage = INVALID_PTHREAD_KEY;
}
}
}

View File

@@ -0,0 +1,226 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#include "../generic/SDL_syscond_c.h"
#include "SDL_sysmutex_c.h"
typedef SDL_Condition *(*pfnSDL_CreateCondition)(void);
typedef void (*pfnSDL_DestroyCondition)(SDL_Condition *);
typedef void (*pfnSDL_SignalCondition)(SDL_Condition *);
typedef void (*pfnSDL_BroadcastCondition)(SDL_Condition *);
typedef bool (*pfnSDL_WaitConditionTimeoutNS)(SDL_Condition *, SDL_Mutex *, Sint64);
typedef struct SDL_cond_impl_t
{
pfnSDL_CreateCondition Create;
pfnSDL_DestroyCondition Destroy;
pfnSDL_SignalCondition Signal;
pfnSDL_BroadcastCondition Broadcast;
pfnSDL_WaitConditionTimeoutNS WaitTimeoutNS;
} SDL_cond_impl_t;
// Implementation will be chosen at runtime based on available Kernel features
static SDL_cond_impl_t SDL_cond_impl_active = { 0 };
/**
* Native Windows Condition Variable (SRW Locks)
*/
#ifndef CONDITION_VARIABLE_INIT
#define CONDITION_VARIABLE_INIT \
{ \
0 \
}
typedef struct CONDITION_VARIABLE
{
PVOID Ptr;
} CONDITION_VARIABLE, *PCONDITION_VARIABLE;
#endif
typedef VOID(WINAPI *pfnWakeConditionVariable)(PCONDITION_VARIABLE);
typedef VOID(WINAPI *pfnWakeAllConditionVariable)(PCONDITION_VARIABLE);
typedef BOOL(WINAPI *pfnSleepConditionVariableSRW)(PCONDITION_VARIABLE, PSRWLOCK, DWORD, ULONG);
typedef BOOL(WINAPI *pfnSleepConditionVariableCS)(PCONDITION_VARIABLE, PCRITICAL_SECTION, DWORD);
static pfnWakeConditionVariable pWakeConditionVariable = NULL;
static pfnWakeAllConditionVariable pWakeAllConditionVariable = NULL;
static pfnSleepConditionVariableSRW pSleepConditionVariableSRW = NULL;
static pfnSleepConditionVariableCS pSleepConditionVariableCS = NULL;
typedef struct SDL_cond_cv
{
CONDITION_VARIABLE cond;
} SDL_cond_cv;
static SDL_Condition *SDL_CreateCondition_cv(void)
{
// Relies on CONDITION_VARIABLE_INIT == 0.
return (SDL_Condition *)SDL_calloc(1, sizeof(SDL_cond_cv));
}
static void SDL_DestroyCondition_cv(SDL_Condition *cond)
{
// There are no kernel allocated resources
SDL_free(cond);
}
static void SDL_SignalCondition_cv(SDL_Condition *_cond)
{
SDL_cond_cv *cond = (SDL_cond_cv *)_cond;
pWakeConditionVariable(&cond->cond);
}
static void SDL_BroadcastCondition_cv(SDL_Condition *_cond)
{
SDL_cond_cv *cond = (SDL_cond_cv *)_cond;
pWakeAllConditionVariable(&cond->cond);
}
static bool SDL_WaitConditionTimeoutNS_cv(SDL_Condition *_cond, SDL_Mutex *_mutex, Sint64 timeoutNS)
{
SDL_cond_cv *cond = (SDL_cond_cv *)_cond;
DWORD timeout;
bool result;
if (timeoutNS < 0) {
timeout = INFINITE;
} else {
timeout = (DWORD)SDL_NS_TO_MS(timeoutNS);
}
if (SDL_mutex_impl_active.Type == SDL_MUTEX_SRW) {
SDL_mutex_srw *mutex = (SDL_mutex_srw *)_mutex;
if (mutex->count != 1 || mutex->owner != GetCurrentThreadId()) {
// Passed mutex is not locked or locked recursively"
return false;
}
// The mutex must be updated to the released state
mutex->count = 0;
mutex->owner = 0;
result = (pSleepConditionVariableSRW(&cond->cond, &mutex->srw, timeout, 0) == TRUE);
// The mutex is owned by us again, regardless of status of the wait
SDL_assert(mutex->count == 0 && mutex->owner == 0);
mutex->count = 1;
mutex->owner = GetCurrentThreadId();
} else {
SDL_mutex_cs *mutex = (SDL_mutex_cs *)_mutex;
SDL_assert(SDL_mutex_impl_active.Type == SDL_MUTEX_CS);
result = (pSleepConditionVariableCS(&cond->cond, &mutex->cs, timeout) == TRUE);
}
return result;
}
static const SDL_cond_impl_t SDL_cond_impl_cv = {
&SDL_CreateCondition_cv,
&SDL_DestroyCondition_cv,
&SDL_SignalCondition_cv,
&SDL_BroadcastCondition_cv,
&SDL_WaitConditionTimeoutNS_cv,
};
// Generic Condition Variable implementation using SDL_Mutex and SDL_Semaphore
static const SDL_cond_impl_t SDL_cond_impl_generic = {
&SDL_CreateCondition_generic,
&SDL_DestroyCondition_generic,
&SDL_SignalCondition_generic,
&SDL_BroadcastCondition_generic,
&SDL_WaitConditionTimeoutNS_generic,
};
SDL_Condition *SDL_CreateCondition(void)
{
if (!SDL_cond_impl_active.Create) {
const SDL_cond_impl_t *impl = NULL;
if (SDL_mutex_impl_active.Type == SDL_MUTEX_INVALID) {
// The mutex implementation isn't decided yet, trigger it
SDL_Mutex *mutex = SDL_CreateMutex();
if (!mutex) {
return NULL;
}
SDL_DestroyMutex(mutex);
SDL_assert(SDL_mutex_impl_active.Type != SDL_MUTEX_INVALID);
}
// Default to generic implementation, works with all mutex implementations
impl = &SDL_cond_impl_generic;
{
HMODULE kernel32 = GetModuleHandle(TEXT("kernel32.dll"));
if (kernel32) {
pWakeConditionVariable = (pfnWakeConditionVariable)GetProcAddress(kernel32, "WakeConditionVariable");
pWakeAllConditionVariable = (pfnWakeAllConditionVariable)GetProcAddress(kernel32, "WakeAllConditionVariable");
pSleepConditionVariableSRW = (pfnSleepConditionVariableSRW)GetProcAddress(kernel32, "SleepConditionVariableSRW");
pSleepConditionVariableCS = (pfnSleepConditionVariableCS)GetProcAddress(kernel32, "SleepConditionVariableCS");
if (pWakeConditionVariable && pWakeAllConditionVariable && pSleepConditionVariableSRW && pSleepConditionVariableCS) {
// Use the Windows provided API
impl = &SDL_cond_impl_cv;
}
}
}
SDL_copyp(&SDL_cond_impl_active, impl);
}
return SDL_cond_impl_active.Create();
}
void SDL_DestroyCondition(SDL_Condition *cond)
{
if (cond) {
SDL_cond_impl_active.Destroy(cond);
}
}
void SDL_SignalCondition(SDL_Condition *cond)
{
if (!cond) {
return;
}
SDL_cond_impl_active.Signal(cond);
}
void SDL_BroadcastCondition(SDL_Condition *cond)
{
if (!cond) {
return;
}
SDL_cond_impl_active.Broadcast(cond);
}
bool SDL_WaitConditionTimeoutNS(SDL_Condition *cond, SDL_Mutex *mutex, Sint64 timeoutNS)
{
if (!cond || !mutex) {
return true;
}
return SDL_cond_impl_active.WaitTimeoutNS(cond, mutex, timeoutNS);
}

View File

@@ -0,0 +1,238 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifdef SDL_THREAD_WINDOWS
/**
* Mutex functions using the Win32 API
* There are two implementations available based on:
* - Critical Sections. Available on all OS versions since Windows XP.
* - Slim Reader/Writer Locks. Requires Windows 7 or newer.
* which are chosen at runtime.
*/
#include "SDL_sysmutex_c.h"
// Implementation will be chosen at runtime based on available Kernel features
SDL_mutex_impl_t SDL_mutex_impl_active = { 0 };
/**
* Implementation based on Slim Reader/Writer (SRW) Locks for Win 7 and newer.
*/
typedef VOID(WINAPI *pfnInitializeSRWLock)(PSRWLOCK);
typedef VOID(WINAPI *pfnReleaseSRWLockExclusive)(PSRWLOCK);
typedef VOID(WINAPI *pfnAcquireSRWLockExclusive)(PSRWLOCK);
typedef BOOLEAN(WINAPI *pfnTryAcquireSRWLockExclusive)(PSRWLOCK);
static pfnInitializeSRWLock pInitializeSRWLock = NULL;
static pfnReleaseSRWLockExclusive pReleaseSRWLockExclusive = NULL;
static pfnAcquireSRWLockExclusive pAcquireSRWLockExclusive = NULL;
static pfnTryAcquireSRWLockExclusive pTryAcquireSRWLockExclusive = NULL;
static SDL_Mutex *SDL_CreateMutex_srw(void)
{
SDL_mutex_srw *mutex = (SDL_mutex_srw *)SDL_calloc(1, sizeof(*mutex));
if (mutex) {
pInitializeSRWLock(&mutex->srw);
}
return (SDL_Mutex *)mutex;
}
static void SDL_DestroyMutex_srw(SDL_Mutex *mutex)
{
// There are no kernel allocated resources
SDL_free(mutex);
}
static void SDL_LockMutex_srw(SDL_Mutex *_mutex) SDL_NO_THREAD_SAFETY_ANALYSIS // clang doesn't know about NULL mutexes
{
SDL_mutex_srw *mutex = (SDL_mutex_srw *)_mutex;
const DWORD this_thread = GetCurrentThreadId();
if (mutex->owner == this_thread) {
++mutex->count;
} else {
/* The order of operations is important.
We set the locking thread id after we obtain the lock
so unlocks from other threads will fail.
*/
pAcquireSRWLockExclusive(&mutex->srw);
SDL_assert(mutex->count == 0 && mutex->owner == 0);
mutex->owner = this_thread;
mutex->count = 1;
}
}
static bool SDL_TryLockMutex_srw(SDL_Mutex *_mutex)
{
SDL_mutex_srw *mutex = (SDL_mutex_srw *)_mutex;
const DWORD this_thread = GetCurrentThreadId();
bool retval = true;
if (mutex->owner == this_thread) {
++mutex->count;
} else {
if (pTryAcquireSRWLockExclusive(&mutex->srw) != 0) {
SDL_assert(mutex->count == 0 && mutex->owner == 0);
mutex->owner = this_thread;
mutex->count = 1;
} else {
retval = false;
}
}
return retval;
}
static void SDL_UnlockMutex_srw(SDL_Mutex *_mutex) SDL_NO_THREAD_SAFETY_ANALYSIS // clang doesn't know about NULL mutexes
{
SDL_mutex_srw *mutex = (SDL_mutex_srw *)_mutex;
if (mutex->owner == GetCurrentThreadId()) {
if (--mutex->count == 0) {
mutex->owner = 0;
pReleaseSRWLockExclusive(&mutex->srw);
}
} else {
SDL_assert(!"mutex not owned by this thread"); // undefined behavior...!
}
}
static const SDL_mutex_impl_t SDL_mutex_impl_srw = {
&SDL_CreateMutex_srw,
&SDL_DestroyMutex_srw,
&SDL_LockMutex_srw,
&SDL_TryLockMutex_srw,
&SDL_UnlockMutex_srw,
SDL_MUTEX_SRW,
};
/**
* Fallback Mutex implementation using Critical Sections (before Win 7)
*/
static SDL_Mutex *SDL_CreateMutex_cs(void)
{
SDL_mutex_cs *mutex = (SDL_mutex_cs *)SDL_malloc(sizeof(*mutex));
if (mutex) {
// Initialize
// On SMP systems, a non-zero spin count generally helps performance
// This function always succeeds
(void)InitializeCriticalSectionAndSpinCount(&mutex->cs, 2000);
}
return (SDL_Mutex *)mutex;
}
static void SDL_DestroyMutex_cs(SDL_Mutex *mutex_)
{
SDL_mutex_cs *mutex = (SDL_mutex_cs *)mutex_;
DeleteCriticalSection(&mutex->cs);
SDL_free(mutex);
}
static void SDL_LockMutex_cs(SDL_Mutex *mutex_) SDL_NO_THREAD_SAFETY_ANALYSIS // clang doesn't know about NULL mutexes
{
SDL_mutex_cs *mutex = (SDL_mutex_cs *)mutex_;
EnterCriticalSection(&mutex->cs);
}
static bool SDL_TryLockMutex_cs(SDL_Mutex *mutex_)
{
SDL_mutex_cs *mutex = (SDL_mutex_cs *)mutex_;
return (TryEnterCriticalSection(&mutex->cs) == TRUE);
}
static void SDL_UnlockMutex_cs(SDL_Mutex *mutex_) SDL_NO_THREAD_SAFETY_ANALYSIS // clang doesn't know about NULL mutexes
{
SDL_mutex_cs *mutex = (SDL_mutex_cs *)mutex_;
LeaveCriticalSection(&mutex->cs);
}
static const SDL_mutex_impl_t SDL_mutex_impl_cs = {
&SDL_CreateMutex_cs,
&SDL_DestroyMutex_cs,
&SDL_LockMutex_cs,
&SDL_TryLockMutex_cs,
&SDL_UnlockMutex_cs,
SDL_MUTEX_CS,
};
/**
* Runtime selection and redirection
*/
SDL_Mutex *SDL_CreateMutex(void)
{
if (!SDL_mutex_impl_active.Create) {
const SDL_mutex_impl_t *impl = &SDL_mutex_impl_cs;
// Try faster implementation for Windows 7 and newer
HMODULE kernel32 = GetModuleHandle(TEXT("kernel32.dll"));
if (kernel32) {
// Requires Vista:
pInitializeSRWLock = (pfnInitializeSRWLock)GetProcAddress(kernel32, "InitializeSRWLock");
pReleaseSRWLockExclusive = (pfnReleaseSRWLockExclusive)GetProcAddress(kernel32, "ReleaseSRWLockExclusive");
pAcquireSRWLockExclusive = (pfnAcquireSRWLockExclusive)GetProcAddress(kernel32, "AcquireSRWLockExclusive");
// Requires 7:
pTryAcquireSRWLockExclusive = (pfnTryAcquireSRWLockExclusive)GetProcAddress(kernel32, "TryAcquireSRWLockExclusive");
if (pInitializeSRWLock && pReleaseSRWLockExclusive && pAcquireSRWLockExclusive && pTryAcquireSRWLockExclusive) {
impl = &SDL_mutex_impl_srw;
}
}
// Copy instead of using pointer to save one level of indirection
SDL_copyp(&SDL_mutex_impl_active, impl);
}
return SDL_mutex_impl_active.Create();
}
void SDL_DestroyMutex(SDL_Mutex *mutex)
{
if (mutex) {
SDL_mutex_impl_active.Destroy(mutex);
}
}
void SDL_LockMutex(SDL_Mutex *mutex)
{
if (mutex) {
SDL_mutex_impl_active.Lock(mutex);
}
}
bool SDL_TryLockMutex(SDL_Mutex *mutex)
{
bool result = true;
if (mutex) {
result = SDL_mutex_impl_active.TryLock(mutex);
}
return result;
}
void SDL_UnlockMutex(SDL_Mutex *mutex)
{
if (mutex) {
SDL_mutex_impl_active.Unlock(mutex);
}
}
#endif // SDL_THREAD_WINDOWS

View File

@@ -0,0 +1,73 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#include "../../core/windows/SDL_windows.h"
typedef SDL_Mutex *(*pfnSDL_CreateMutex)(void);
typedef void (*pfnSDL_LockMutex)(SDL_Mutex *);
typedef bool (*pfnSDL_TryLockMutex)(SDL_Mutex *);
typedef void (*pfnSDL_UnlockMutex)(SDL_Mutex *);
typedef void (*pfnSDL_DestroyMutex)(SDL_Mutex *);
typedef enum
{
SDL_MUTEX_INVALID = 0,
SDL_MUTEX_SRW,
SDL_MUTEX_CS,
} SDL_MutexType;
typedef struct SDL_mutex_impl_t
{
pfnSDL_CreateMutex Create;
pfnSDL_DestroyMutex Destroy;
pfnSDL_LockMutex Lock;
pfnSDL_TryLockMutex TryLock;
pfnSDL_UnlockMutex Unlock;
// Needed by SDL_Condition:
SDL_MutexType Type;
} SDL_mutex_impl_t;
extern SDL_mutex_impl_t SDL_mutex_impl_active;
#ifndef SRWLOCK_INIT
#define SRWLOCK_INIT \
{ \
0 \
}
typedef struct _SRWLOCK
{
PVOID Ptr;
} SRWLOCK, *PSRWLOCK;
#endif
typedef struct SDL_mutex_srw
{
SRWLOCK srw;
// SRW Locks are not recursive, that has to be handled by SDL:
DWORD count;
DWORD owner;
} SDL_mutex_srw;
typedef struct SDL_mutex_cs
{
CRITICAL_SECTION cs;
} SDL_mutex_cs;

View File

@@ -0,0 +1,231 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
/**
* Implementation based on Slim Reader/Writer (SRW) Locks for Win 7 and newer.
*/
// This header makes sure SRWLOCK is actually declared, even on ancient WinSDKs.
#include "SDL_sysmutex_c.h"
typedef VOID(WINAPI *pfnInitializeSRWLock)(PSRWLOCK);
typedef VOID(WINAPI *pfnReleaseSRWLockShared)(PSRWLOCK);
typedef VOID(WINAPI *pfnAcquireSRWLockShared)(PSRWLOCK);
typedef BOOLEAN(WINAPI *pfnTryAcquireSRWLockShared)(PSRWLOCK);
typedef VOID(WINAPI *pfnReleaseSRWLockExclusive)(PSRWLOCK);
typedef VOID(WINAPI *pfnAcquireSRWLockExclusive)(PSRWLOCK);
typedef BOOLEAN(WINAPI *pfnTryAcquireSRWLockExclusive)(PSRWLOCK);
static pfnInitializeSRWLock pInitializeSRWLock = NULL;
static pfnReleaseSRWLockShared pReleaseSRWLockShared = NULL;
static pfnAcquireSRWLockShared pAcquireSRWLockShared = NULL;
static pfnTryAcquireSRWLockShared pTryAcquireSRWLockShared = NULL;
static pfnReleaseSRWLockExclusive pReleaseSRWLockExclusive = NULL;
static pfnAcquireSRWLockExclusive pAcquireSRWLockExclusive = NULL;
static pfnTryAcquireSRWLockExclusive pTryAcquireSRWLockExclusive = NULL;
typedef SDL_RWLock *(*pfnSDL_CreateRWLock)(void);
typedef void (*pfnSDL_DestroyRWLock)(SDL_RWLock *);
typedef void (*pfnSDL_LockRWLockForReading)(SDL_RWLock *);
typedef void (*pfnSDL_LockRWLockForWriting)(SDL_RWLock *);
typedef bool (*pfnSDL_TryLockRWLockForReading)(SDL_RWLock *);
typedef bool (*pfnSDL_TryLockRWLockForWriting)(SDL_RWLock *);
typedef void (*pfnSDL_UnlockRWLock)(SDL_RWLock *);
typedef struct SDL_rwlock_impl_t
{
pfnSDL_CreateRWLock Create;
pfnSDL_DestroyRWLock Destroy;
pfnSDL_LockRWLockForReading LockForReading;
pfnSDL_LockRWLockForWriting LockForWriting;
pfnSDL_TryLockRWLockForReading TryLockForReading;
pfnSDL_TryLockRWLockForWriting TryLockForWriting;
pfnSDL_UnlockRWLock Unlock;
} SDL_rwlock_impl_t;
// Implementation will be chosen at runtime based on available Kernel features
static SDL_rwlock_impl_t SDL_rwlock_impl_active = { 0 };
// rwlock implementation using Win7+ slim read/write locks (SRWLOCK)
typedef struct SDL_rwlock_srw
{
SRWLOCK srw;
SDL_ThreadID write_owner;
} SDL_rwlock_srw;
static SDL_RWLock *SDL_CreateRWLock_srw(void)
{
SDL_rwlock_srw *rwlock = (SDL_rwlock_srw *)SDL_calloc(1, sizeof(*rwlock));
if (rwlock) {
pInitializeSRWLock(&rwlock->srw);
}
return (SDL_RWLock *)rwlock;
}
static void SDL_DestroyRWLock_srw(SDL_RWLock *_rwlock)
{
SDL_rwlock_srw *rwlock = (SDL_rwlock_srw *) _rwlock;
// There are no kernel allocated resources
SDL_free(rwlock);
}
static void SDL_LockRWLockForReading_srw(SDL_RWLock *_rwlock) SDL_NO_THREAD_SAFETY_ANALYSIS // clang doesn't know about NULL mutexes
{
SDL_rwlock_srw *rwlock = (SDL_rwlock_srw *) _rwlock;
pAcquireSRWLockShared(&rwlock->srw);
}
static void SDL_LockRWLockForWriting_srw(SDL_RWLock *_rwlock) SDL_NO_THREAD_SAFETY_ANALYSIS // clang doesn't know about NULL mutexes
{
SDL_rwlock_srw *rwlock = (SDL_rwlock_srw *) _rwlock;
pAcquireSRWLockExclusive(&rwlock->srw);
rwlock->write_owner = SDL_GetCurrentThreadID();
}
static bool SDL_TryLockRWLockForReading_srw(SDL_RWLock *_rwlock)
{
SDL_rwlock_srw *rwlock = (SDL_rwlock_srw *) _rwlock;
return pTryAcquireSRWLockShared(&rwlock->srw);
}
static bool SDL_TryLockRWLockForWriting_srw(SDL_RWLock *_rwlock)
{
SDL_rwlock_srw *rwlock = (SDL_rwlock_srw *) _rwlock;
if (pTryAcquireSRWLockExclusive(&rwlock->srw)) {
rwlock->write_owner = SDL_GetCurrentThreadID();
return true;
} else {
return false;
}
}
static void SDL_UnlockRWLock_srw(SDL_RWLock *_rwlock) SDL_NO_THREAD_SAFETY_ANALYSIS // clang doesn't know about NULL mutexes
{
SDL_rwlock_srw *rwlock = (SDL_rwlock_srw *) _rwlock;
if (rwlock->write_owner == SDL_GetCurrentThreadID()) {
rwlock->write_owner = 0;
pReleaseSRWLockExclusive(&rwlock->srw);
} else {
pReleaseSRWLockShared(&rwlock->srw);
}
}
static const SDL_rwlock_impl_t SDL_rwlock_impl_srw = {
&SDL_CreateRWLock_srw,
&SDL_DestroyRWLock_srw,
&SDL_LockRWLockForReading_srw,
&SDL_LockRWLockForWriting_srw,
&SDL_TryLockRWLockForReading_srw,
&SDL_TryLockRWLockForWriting_srw,
&SDL_UnlockRWLock_srw
};
#include "../generic/SDL_sysrwlock_c.h"
// Generic rwlock implementation using SDL_Mutex, SDL_Condition, and SDL_AtomicInt
static const SDL_rwlock_impl_t SDL_rwlock_impl_generic = {
&SDL_CreateRWLock_generic,
&SDL_DestroyRWLock_generic,
&SDL_LockRWLockForReading_generic,
&SDL_LockRWLockForWriting_generic,
&SDL_TryLockRWLockForReading_generic,
&SDL_TryLockRWLockForWriting_generic,
&SDL_UnlockRWLock_generic
};
SDL_RWLock *SDL_CreateRWLock(void)
{
if (!SDL_rwlock_impl_active.Create) {
// Default to generic implementation, works with all mutex implementations
const SDL_rwlock_impl_t *impl = &SDL_rwlock_impl_generic;
{
HMODULE kernel32 = GetModuleHandle(TEXT("kernel32.dll"));
if (kernel32) {
bool okay = true;
#define LOOKUP_SRW_SYM(sym) if (okay) { if ((p##sym = (pfn##sym)GetProcAddress(kernel32, #sym)) == NULL) { okay = false; } }
LOOKUP_SRW_SYM(InitializeSRWLock);
LOOKUP_SRW_SYM(ReleaseSRWLockShared);
LOOKUP_SRW_SYM(AcquireSRWLockShared);
LOOKUP_SRW_SYM(TryAcquireSRWLockShared);
LOOKUP_SRW_SYM(ReleaseSRWLockExclusive);
LOOKUP_SRW_SYM(AcquireSRWLockExclusive);
LOOKUP_SRW_SYM(TryAcquireSRWLockExclusive);
#undef LOOKUP_SRW_SYM
if (okay) {
impl = &SDL_rwlock_impl_srw; // Use the Windows provided API instead of generic fallback
}
}
}
SDL_copyp(&SDL_rwlock_impl_active, impl);
}
return SDL_rwlock_impl_active.Create();
}
void SDL_DestroyRWLock(SDL_RWLock *rwlock)
{
if (rwlock) {
SDL_rwlock_impl_active.Destroy(rwlock);
}
}
void SDL_LockRWLockForReading(SDL_RWLock *rwlock) SDL_NO_THREAD_SAFETY_ANALYSIS // clang doesn't know about NULL mutexes
{
if (rwlock) {
SDL_rwlock_impl_active.LockForReading(rwlock);
}
}
void SDL_LockRWLockForWriting(SDL_RWLock *rwlock) SDL_NO_THREAD_SAFETY_ANALYSIS // clang doesn't know about NULL mutexes
{
if (rwlock) {
SDL_rwlock_impl_active.LockForWriting(rwlock);
}
}
bool SDL_TryLockRWLockForReading(SDL_RWLock *rwlock)
{
bool result = true;
if (rwlock) {
result = SDL_rwlock_impl_active.TryLockForReading(rwlock);
}
return result;
}
bool SDL_TryLockRWLockForWriting(SDL_RWLock *rwlock)
{
bool result = true;
if (rwlock) {
result = SDL_rwlock_impl_active.TryLockForWriting(rwlock);
}
return result;
}
void SDL_UnlockRWLock(SDL_RWLock *rwlock) SDL_NO_THREAD_SAFETY_ANALYSIS // clang doesn't know about NULL mutexes
{
if (rwlock) {
SDL_rwlock_impl_active.Unlock(rwlock);
}
}

View File

@@ -0,0 +1,351 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifdef SDL_THREAD_WINDOWS
/**
* Semaphore functions using the Win32 API
* There are two implementations available based on:
* - Kernel Semaphores. Available on all OS versions. (kern)
* Heavy-weight inter-process kernel objects.
* - Atomics and WaitOnAddress API. (atom)
* Faster due to significantly less context switches.
* Requires Windows 8 or newer.
* which are chosen at runtime.
*/
#include "../../core/windows/SDL_windows.h"
typedef SDL_Semaphore *(*pfnSDL_CreateSemaphore)(Uint32);
typedef void (*pfnSDL_DestroySemaphore)(SDL_Semaphore *);
typedef bool (*pfnSDL_WaitSemaphoreTimeoutNS)(SDL_Semaphore *, Sint64);
typedef Uint32 (*pfnSDL_GetSemaphoreValue)(SDL_Semaphore *);
typedef void (*pfnSDL_SignalSemaphore)(SDL_Semaphore *);
typedef struct SDL_semaphore_impl_t
{
pfnSDL_CreateSemaphore Create;
pfnSDL_DestroySemaphore Destroy;
pfnSDL_WaitSemaphoreTimeoutNS WaitTimeoutNS;
pfnSDL_GetSemaphoreValue Value;
pfnSDL_SignalSemaphore Signal;
} SDL_sem_impl_t;
// Implementation will be chosen at runtime based on available Kernel features
static SDL_sem_impl_t SDL_sem_impl_active = { 0 };
/**
* Atomic + WaitOnAddress implementation
*/
// APIs not available on WinPhone 8.1
// https://www.microsoft.com/en-us/download/details.aspx?id=47328
typedef BOOL(WINAPI *pfnWaitOnAddress)(volatile VOID *, PVOID, SIZE_T, DWORD);
typedef VOID(WINAPI *pfnWakeByAddressSingle)(PVOID);
static pfnWaitOnAddress pWaitOnAddress = NULL;
static pfnWakeByAddressSingle pWakeByAddressSingle = NULL;
typedef struct SDL_semaphore_atom
{
LONG count;
} SDL_sem_atom;
static SDL_Semaphore *SDL_CreateSemaphore_atom(Uint32 initial_value)
{
SDL_sem_atom *sem;
sem = (SDL_sem_atom *)SDL_malloc(sizeof(*sem));
if (sem) {
sem->count = initial_value;
}
return (SDL_Semaphore *)sem;
}
static void SDL_DestroySemaphore_atom(SDL_Semaphore *sem)
{
SDL_free(sem);
}
static bool SDL_WaitSemaphoreTimeoutNS_atom(SDL_Semaphore *_sem, Sint64 timeoutNS)
{
SDL_sem_atom *sem = (SDL_sem_atom *)_sem;
LONG count;
Uint64 now;
Uint64 deadline;
DWORD timeout_eff;
if (!sem) {
return true;
}
if (timeoutNS == 0) {
count = sem->count;
if (count == 0) {
return false;
}
if (InterlockedCompareExchange(&sem->count, count - 1, count) == count) {
return true;
}
return false;
}
if (timeoutNS < 0) {
for (;;) {
count = sem->count;
while (count == 0) {
if (!pWaitOnAddress(&sem->count, &count, sizeof(sem->count), INFINITE)) {
return false;
}
count = sem->count;
}
if (InterlockedCompareExchange(&sem->count, count - 1, count) == count) {
return true;
}
}
}
/**
* WaitOnAddress is subject to spurious and stolen wakeups so we
* need to recalculate the effective timeout before every wait
*/
now = SDL_GetTicksNS();
deadline = now + timeoutNS;
for (;;) {
count = sem->count;
// If no semaphore is available we need to wait
while (count == 0) {
now = SDL_GetTicksNS();
if (deadline > now) {
timeout_eff = (DWORD)SDL_NS_TO_MS(deadline - now);
} else {
return false;
}
if (!pWaitOnAddress(&sem->count, &count, sizeof(count), timeout_eff)) {
return false;
}
count = sem->count;
}
// Actually the semaphore is only consumed if this succeeds
// If it doesn't we need to do everything again
if (InterlockedCompareExchange(&sem->count, count - 1, count) == count) {
return true;
}
}
}
static Uint32 SDL_GetSemaphoreValue_atom(SDL_Semaphore *_sem)
{
SDL_sem_atom *sem = (SDL_sem_atom *)_sem;
if (!sem) {
return 0;
}
return (Uint32)sem->count;
}
static void SDL_SignalSemaphore_atom(SDL_Semaphore *_sem)
{
SDL_sem_atom *sem = (SDL_sem_atom *)_sem;
if (!sem) {
return;
}
InterlockedIncrement(&sem->count);
pWakeByAddressSingle(&sem->count);
}
static const SDL_sem_impl_t SDL_sem_impl_atom = {
&SDL_CreateSemaphore_atom,
&SDL_DestroySemaphore_atom,
&SDL_WaitSemaphoreTimeoutNS_atom,
&SDL_GetSemaphoreValue_atom,
&SDL_SignalSemaphore_atom,
};
/**
* Fallback Semaphore implementation using Kernel Semaphores
*/
typedef struct SDL_semaphore_kern
{
HANDLE id;
LONG count;
} SDL_sem_kern;
// Create a semaphore
static SDL_Semaphore *SDL_CreateSemaphore_kern(Uint32 initial_value)
{
SDL_sem_kern *sem;
// Allocate sem memory
sem = (SDL_sem_kern *)SDL_malloc(sizeof(*sem));
if (sem) {
// Create the semaphore, with max value 32K
sem->id = CreateSemaphore(NULL, initial_value, 32 * 1024, NULL);
sem->count = initial_value;
if (!sem->id) {
SDL_SetError("Couldn't create semaphore");
SDL_free(sem);
sem = NULL;
}
}
return (SDL_Semaphore *)sem;
}
// Free the semaphore
static void SDL_DestroySemaphore_kern(SDL_Semaphore *_sem)
{
SDL_sem_kern *sem = (SDL_sem_kern *)_sem;
if (sem) {
if (sem->id) {
CloseHandle(sem->id);
sem->id = 0;
}
SDL_free(sem);
}
}
static bool SDL_WaitSemaphoreTimeoutNS_kern(SDL_Semaphore *_sem, Sint64 timeoutNS)
{
SDL_sem_kern *sem = (SDL_sem_kern *)_sem;
DWORD dwMilliseconds;
if (!sem) {
return true;
}
if (timeoutNS < 0) {
dwMilliseconds = INFINITE;
} else {
dwMilliseconds = (DWORD)SDL_NS_TO_MS(timeoutNS);
}
switch (WaitForSingleObjectEx(sem->id, dwMilliseconds, FALSE)) {
case WAIT_OBJECT_0:
InterlockedDecrement(&sem->count);
return true;
default:
return false;
}
}
// Returns the current count of the semaphore
static Uint32 SDL_GetSemaphoreValue_kern(SDL_Semaphore *_sem)
{
SDL_sem_kern *sem = (SDL_sem_kern *)_sem;
if (!sem) {
return 0;
}
return (Uint32)sem->count;
}
static void SDL_SignalSemaphore_kern(SDL_Semaphore *_sem)
{
SDL_sem_kern *sem = (SDL_sem_kern *)_sem;
if (!sem) {
return;
}
/* Increase the counter in the first place, because
* after a successful release the semaphore may
* immediately get destroyed by another thread which
* is waiting for this semaphore.
*/
InterlockedIncrement(&sem->count);
if (ReleaseSemaphore(sem->id, 1, NULL) == FALSE) {
InterlockedDecrement(&sem->count); // restore
}
}
static const SDL_sem_impl_t SDL_sem_impl_kern = {
&SDL_CreateSemaphore_kern,
&SDL_DestroySemaphore_kern,
&SDL_WaitSemaphoreTimeoutNS_kern,
&SDL_GetSemaphoreValue_kern,
&SDL_SignalSemaphore_kern,
};
/**
* Runtime selection and redirection
*/
SDL_Semaphore *SDL_CreateSemaphore(Uint32 initial_value)
{
if (!SDL_sem_impl_active.Create) {
// Default to fallback implementation
const SDL_sem_impl_t *impl = &SDL_sem_impl_kern;
if (!SDL_GetHintBoolean(SDL_HINT_WINDOWS_FORCE_SEMAPHORE_KERNEL, false)) {
/* We already statically link to features from this Api
* Set (e.g. WaitForSingleObject). Dynamically loading
* API Sets is not explicitly documented but according to
* Microsoft our specific use case is legal and correct:
* https://github.com/microsoft/STL/pull/593#issuecomment-655799859
*/
HMODULE synch120 = GetModuleHandle(TEXT("api-ms-win-core-synch-l1-2-0.dll"));
if (synch120) {
// Try to load required functions provided by Win 8 or newer
pWaitOnAddress = (pfnWaitOnAddress)GetProcAddress(synch120, "WaitOnAddress");
pWakeByAddressSingle = (pfnWakeByAddressSingle)GetProcAddress(synch120, "WakeByAddressSingle");
if (pWaitOnAddress && pWakeByAddressSingle) {
impl = &SDL_sem_impl_atom;
}
}
}
// Copy instead of using pointer to save one level of indirection
SDL_copyp(&SDL_sem_impl_active, impl);
}
return SDL_sem_impl_active.Create(initial_value);
}
void SDL_DestroySemaphore(SDL_Semaphore *sem)
{
SDL_sem_impl_active.Destroy(sem);
}
bool SDL_WaitSemaphoreTimeoutNS(SDL_Semaphore *sem, Sint64 timeoutNS)
{
return SDL_sem_impl_active.WaitTimeoutNS(sem, timeoutNS);
}
Uint32 SDL_GetSemaphoreValue(SDL_Semaphore *sem)
{
return SDL_sem_impl_active.Value(sem);
}
void SDL_SignalSemaphore(SDL_Semaphore *sem)
{
SDL_sem_impl_active.Signal(sem);
}
#endif // SDL_THREAD_WINDOWS

View File

@@ -0,0 +1,197 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifdef SDL_THREAD_WINDOWS
// Win32 thread management routines for SDL
#include "../SDL_thread_c.h"
#include "../SDL_systhread.h"
#include "SDL_systhread_c.h"
#ifndef STACK_SIZE_PARAM_IS_A_RESERVATION
#define STACK_SIZE_PARAM_IS_A_RESERVATION 0x00010000
#endif
#define SDL_DEBUGGER_NAME_EXCEPTION_CODE 0x406D1388
typedef void (__cdecl * SDL_EndThreadExCallback) (unsigned retval);
typedef uintptr_t (__cdecl * SDL_BeginThreadExCallback)
(void *security, unsigned stacksize, unsigned (__stdcall *startaddr)(void *),
void * arglist, unsigned initflag, unsigned *threadaddr);
static DWORD RunThread(void *data)
{
SDL_Thread *thread = (SDL_Thread *)data;
SDL_EndThreadExCallback pfnEndThread = (SDL_EndThreadExCallback)thread->endfunc;
SDL_RunThread(thread);
if (pfnEndThread) {
pfnEndThread(0);
}
return 0;
}
static DWORD WINAPI MINGW32_FORCEALIGN RunThreadViaCreateThread(LPVOID data)
{
return RunThread(data);
}
static unsigned __stdcall MINGW32_FORCEALIGN RunThreadViaBeginThreadEx(void *data)
{
return (unsigned)RunThread(data);
}
bool SDL_SYS_CreateThread(SDL_Thread *thread,
SDL_FunctionPointer vpfnBeginThread,
SDL_FunctionPointer vpfnEndThread)
{
SDL_BeginThreadExCallback pfnBeginThread = (SDL_BeginThreadExCallback) vpfnBeginThread;
const DWORD flags = thread->stacksize ? STACK_SIZE_PARAM_IS_A_RESERVATION : 0;
// Save the function which we will have to call to clear the RTL of calling app!
thread->endfunc = vpfnEndThread;
// thread->stacksize == 0 means "system default", same as win32 expects
if (pfnBeginThread) {
unsigned threadid = 0;
thread->handle = (SYS_ThreadHandle)((size_t)pfnBeginThread(NULL, (unsigned int)thread->stacksize,
RunThreadViaBeginThreadEx,
thread, flags, &threadid));
} else {
DWORD threadid = 0;
thread->handle = CreateThread(NULL, thread->stacksize,
RunThreadViaCreateThread,
thread, flags, &threadid);
}
if (!thread->handle) {
return SDL_SetError("Not enough resources to create thread");
}
return true;
}
#pragma pack(push, 8)
typedef struct tagTHREADNAME_INFO
{
DWORD dwType; // must be 0x1000
LPCSTR szName; // pointer to name (in user addr space)
DWORD dwThreadID; // thread ID (-1=caller thread)
DWORD dwFlags; // reserved for future use, must be zero
} THREADNAME_INFO;
#pragma pack(pop)
static LONG NTAPI EmptyVectoredExceptionHandler(EXCEPTION_POINTERS *info)
{
if (info != NULL && info->ExceptionRecord != NULL && info->ExceptionRecord->ExceptionCode == SDL_DEBUGGER_NAME_EXCEPTION_CODE) {
return EXCEPTION_CONTINUE_EXECUTION;
} else {
return EXCEPTION_CONTINUE_SEARCH;
}
}
typedef HRESULT(WINAPI *pfnSetThreadDescription)(HANDLE, PCWSTR);
void SDL_SYS_SetupThread(const char *name)
{
if (name) {
PVOID exceptionHandlerHandle;
static pfnSetThreadDescription pSetThreadDescription = NULL;
static HMODULE kernel32 = NULL;
if (!kernel32) {
kernel32 = GetModuleHandle(TEXT("kernel32.dll"));
if (kernel32) {
pSetThreadDescription = (pfnSetThreadDescription)GetProcAddress(kernel32, "SetThreadDescription");
}
if (!kernel32 || !pSetThreadDescription) {
HMODULE kernelBase = GetModuleHandle(TEXT("KernelBase.dll"));
if (kernelBase) {
pSetThreadDescription = (pfnSetThreadDescription)GetProcAddress(kernelBase, "SetThreadDescription");
}
}
}
if (pSetThreadDescription) {
WCHAR *strw = WIN_UTF8ToStringW(name);
if (strw) {
pSetThreadDescription(GetCurrentThread(), strw);
SDL_free(strw);
}
}
/* Presumably some version of Visual Studio will understand SetThreadDescription(),
but we still need to deal with older OSes and debuggers. Set it with the arcane
exception magic, too. */
exceptionHandlerHandle = AddVectoredExceptionHandler(1, EmptyVectoredExceptionHandler);
if (exceptionHandlerHandle) {
THREADNAME_INFO inf;
// This magic tells the debugger to name a thread if it's listening.
SDL_zero(inf);
inf.dwType = 0x1000;
inf.szName = name;
inf.dwThreadID = (DWORD)-1;
inf.dwFlags = 0;
// The debugger catches this, renames the thread, continues on.
RaiseException(SDL_DEBUGGER_NAME_EXCEPTION_CODE, 0, sizeof(inf) / sizeof(ULONG_PTR), (const ULONG_PTR *)&inf);
RemoveVectoredExceptionHandler(exceptionHandlerHandle);
}
}
}
SDL_ThreadID SDL_GetCurrentThreadID(void)
{
return (SDL_ThreadID)GetCurrentThreadId();
}
bool SDL_SYS_SetThreadPriority(SDL_ThreadPriority priority)
{
int value;
if (priority == SDL_THREAD_PRIORITY_LOW) {
value = THREAD_PRIORITY_LOWEST;
} else if (priority == SDL_THREAD_PRIORITY_HIGH) {
value = THREAD_PRIORITY_HIGHEST;
} else if (priority == SDL_THREAD_PRIORITY_TIME_CRITICAL) {
value = THREAD_PRIORITY_TIME_CRITICAL;
} else {
value = THREAD_PRIORITY_NORMAL;
}
if (!SetThreadPriority(GetCurrentThread(), value)) {
return WIN_SetError("SetThreadPriority()");
}
return true;
}
void SDL_SYS_WaitThread(SDL_Thread *thread)
{
WaitForSingleObjectEx(thread->handle, INFINITE, FALSE);
CloseHandle(thread->handle);
}
void SDL_SYS_DetachThread(SDL_Thread *thread)
{
CloseHandle(thread->handle);
}
#endif // SDL_THREAD_WINDOWS

View File

@@ -0,0 +1,30 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifndef SDL_systhread_c_h_
#define SDL_systhread_c_h_
#include "../../core/windows/SDL_windows.h"
typedef HANDLE SYS_ThreadHandle;
#endif // SDL_systhread_c_h_

View File

@@ -0,0 +1,81 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifdef SDL_THREAD_WINDOWS
#include "../../core/windows/SDL_windows.h"
#include "../SDL_thread_c.h"
static DWORD thread_local_storage = TLS_OUT_OF_INDEXES;
static bool generic_local_storage = false;
void SDL_SYS_InitTLSData(void)
{
if (thread_local_storage == TLS_OUT_OF_INDEXES && !generic_local_storage) {
thread_local_storage = TlsAlloc();
if (thread_local_storage == TLS_OUT_OF_INDEXES) {
SDL_Generic_InitTLSData();
generic_local_storage = true;
}
}
}
SDL_TLSData *SDL_SYS_GetTLSData(void)
{
if (generic_local_storage) {
return SDL_Generic_GetTLSData();
}
if (thread_local_storage != TLS_OUT_OF_INDEXES) {
return (SDL_TLSData *)TlsGetValue(thread_local_storage);
}
return NULL;
}
bool SDL_SYS_SetTLSData(SDL_TLSData *data)
{
if (generic_local_storage) {
return SDL_Generic_SetTLSData(data);
}
if (!TlsSetValue(thread_local_storage, data)) {
return WIN_SetError("TlsSetValue()");
}
return true;
}
void SDL_SYS_QuitTLSData(void)
{
if (generic_local_storage) {
SDL_Generic_QuitTLSData();
generic_local_storage = false;
} else {
if (thread_local_storage != TLS_OUT_OF_INDEXES) {
TlsFree(thread_local_storage);
thread_local_storage = TLS_OUT_OF_INDEXES;
}
}
}
#endif // SDL_THREAD_WINDOWS