From 5a079a2d114f96d4847d1ee305d5b7c16eeec50e Mon Sep 17 00:00:00 2001 From: 3gg <3gg@shellblade.net> Date: Sat, 27 Dec 2025 12:03:39 -0800 Subject: Initial commit --- contrib/SDL-3.2.8/src/thread/SDL_systhread.h | 77 +++ contrib/SDL-3.2.8/src/thread/SDL_thread.c | 584 +++++++++++++++++++++ contrib/SDL-3.2.8/src/thread/SDL_thread_c.h | 92 ++++ contrib/SDL-3.2.8/src/thread/generic/SDL_syscond.c | 216 ++++++++ .../SDL-3.2.8/src/thread/generic/SDL_syscond_c.h | 36 ++ .../SDL-3.2.8/src/thread/generic/SDL_sysmutex.c | 132 +++++ .../SDL-3.2.8/src/thread/generic/SDL_sysmutex_c.h | 21 + .../SDL-3.2.8/src/thread/generic/SDL_sysrwlock.c | 194 +++++++ .../SDL-3.2.8/src/thread/generic/SDL_sysrwlock_c.h | 38 ++ contrib/SDL-3.2.8/src/thread/generic/SDL_syssem.c | 183 +++++++ .../SDL-3.2.8/src/thread/generic/SDL_systhread.c | 57 ++ .../SDL-3.2.8/src/thread/generic/SDL_systhread_c.h | 24 + contrib/SDL-3.2.8/src/thread/generic/SDL_systls.c | 44 ++ contrib/SDL-3.2.8/src/thread/n3ds/SDL_sysmutex.c | 67 +++ contrib/SDL-3.2.8/src/thread/n3ds/SDL_sysmutex_c.h | 33 ++ contrib/SDL-3.2.8/src/thread/n3ds/SDL_syssem.c | 112 ++++ contrib/SDL-3.2.8/src/thread/n3ds/SDL_systhread.c | 139 +++++ .../SDL-3.2.8/src/thread/n3ds/SDL_systhread_c.h | 30 ++ contrib/SDL-3.2.8/src/thread/ps2/SDL_syssem.c | 122 +++++ contrib/SDL-3.2.8/src/thread/ps2/SDL_systhread.c | 143 +++++ contrib/SDL-3.2.8/src/thread/ps2/SDL_systhread_c.h | 22 + contrib/SDL-3.2.8/src/thread/psp/SDL_sysmutex.c | 100 ++++ contrib/SDL-3.2.8/src/thread/psp/SDL_sysmutex_c.h | 21 + contrib/SDL-3.2.8/src/thread/psp/SDL_syssem.c | 119 +++++ contrib/SDL-3.2.8/src/thread/psp/SDL_systhread.c | 112 ++++ contrib/SDL-3.2.8/src/thread/psp/SDL_systhread_c.h | 24 + contrib/SDL-3.2.8/src/thread/pthread/SDL_syscond.c | 128 +++++ .../SDL-3.2.8/src/thread/pthread/SDL_sysmutex.c | 154 ++++++ .../SDL-3.2.8/src/thread/pthread/SDL_sysmutex_c.h | 40 ++ .../SDL-3.2.8/src/thread/pthread/SDL_sysrwlock.c | 113 ++++ contrib/SDL-3.2.8/src/thread/pthread/SDL_syssem.c | 160 ++++++ .../SDL-3.2.8/src/thread/pthread/SDL_systhread.c | 293 +++++++++++ .../SDL-3.2.8/src/thread/pthread/SDL_systhread_c.h | 25 + contrib/SDL-3.2.8/src/thread/pthread/SDL_systls.c | 78 +++ contrib/SDL-3.2.8/src/thread/vita/SDL_sysmutex.c | 97 ++++ contrib/SDL-3.2.8/src/thread/vita/SDL_sysmutex_c.h | 21 + contrib/SDL-3.2.8/src/thread/vita/SDL_syssem.c | 122 +++++ contrib/SDL-3.2.8/src/thread/vita/SDL_systhread.c | 139 +++++ .../SDL-3.2.8/src/thread/vita/SDL_systhread_c.h | 24 + .../SDL-3.2.8/src/thread/windows/SDL_syscond_cv.c | 226 ++++++++ .../SDL-3.2.8/src/thread/windows/SDL_sysmutex.c | 238 +++++++++ .../SDL-3.2.8/src/thread/windows/SDL_sysmutex_c.h | 73 +++ .../src/thread/windows/SDL_sysrwlock_srw.c | 231 ++++++++ contrib/SDL-3.2.8/src/thread/windows/SDL_syssem.c | 351 +++++++++++++ .../SDL-3.2.8/src/thread/windows/SDL_systhread.c | 197 +++++++ .../SDL-3.2.8/src/thread/windows/SDL_systhread_c.h | 30 ++ contrib/SDL-3.2.8/src/thread/windows/SDL_systls.c | 81 +++ 47 files changed, 5563 insertions(+) create mode 100644 contrib/SDL-3.2.8/src/thread/SDL_systhread.h create mode 100644 contrib/SDL-3.2.8/src/thread/SDL_thread.c create mode 100644 contrib/SDL-3.2.8/src/thread/SDL_thread_c.h create mode 100644 contrib/SDL-3.2.8/src/thread/generic/SDL_syscond.c create mode 100644 contrib/SDL-3.2.8/src/thread/generic/SDL_syscond_c.h create mode 100644 contrib/SDL-3.2.8/src/thread/generic/SDL_sysmutex.c create mode 100644 contrib/SDL-3.2.8/src/thread/generic/SDL_sysmutex_c.h create mode 100644 contrib/SDL-3.2.8/src/thread/generic/SDL_sysrwlock.c create mode 100644 contrib/SDL-3.2.8/src/thread/generic/SDL_sysrwlock_c.h create mode 100644 contrib/SDL-3.2.8/src/thread/generic/SDL_syssem.c create mode 100644 contrib/SDL-3.2.8/src/thread/generic/SDL_systhread.c create mode 100644 contrib/SDL-3.2.8/src/thread/generic/SDL_systhread_c.h create mode 100644 contrib/SDL-3.2.8/src/thread/generic/SDL_systls.c create mode 100644 contrib/SDL-3.2.8/src/thread/n3ds/SDL_sysmutex.c create mode 100644 contrib/SDL-3.2.8/src/thread/n3ds/SDL_sysmutex_c.h create mode 100644 contrib/SDL-3.2.8/src/thread/n3ds/SDL_syssem.c create mode 100644 contrib/SDL-3.2.8/src/thread/n3ds/SDL_systhread.c create mode 100644 contrib/SDL-3.2.8/src/thread/n3ds/SDL_systhread_c.h create mode 100644 contrib/SDL-3.2.8/src/thread/ps2/SDL_syssem.c create mode 100644 contrib/SDL-3.2.8/src/thread/ps2/SDL_systhread.c create mode 100644 contrib/SDL-3.2.8/src/thread/ps2/SDL_systhread_c.h create mode 100644 contrib/SDL-3.2.8/src/thread/psp/SDL_sysmutex.c create mode 100644 contrib/SDL-3.2.8/src/thread/psp/SDL_sysmutex_c.h create mode 100644 contrib/SDL-3.2.8/src/thread/psp/SDL_syssem.c create mode 100644 contrib/SDL-3.2.8/src/thread/psp/SDL_systhread.c create mode 100644 contrib/SDL-3.2.8/src/thread/psp/SDL_systhread_c.h create mode 100644 contrib/SDL-3.2.8/src/thread/pthread/SDL_syscond.c create mode 100644 contrib/SDL-3.2.8/src/thread/pthread/SDL_sysmutex.c create mode 100644 contrib/SDL-3.2.8/src/thread/pthread/SDL_sysmutex_c.h create mode 100644 contrib/SDL-3.2.8/src/thread/pthread/SDL_sysrwlock.c create mode 100644 contrib/SDL-3.2.8/src/thread/pthread/SDL_syssem.c create mode 100644 contrib/SDL-3.2.8/src/thread/pthread/SDL_systhread.c create mode 100644 contrib/SDL-3.2.8/src/thread/pthread/SDL_systhread_c.h create mode 100644 contrib/SDL-3.2.8/src/thread/pthread/SDL_systls.c create mode 100644 contrib/SDL-3.2.8/src/thread/vita/SDL_sysmutex.c create mode 100644 contrib/SDL-3.2.8/src/thread/vita/SDL_sysmutex_c.h create mode 100644 contrib/SDL-3.2.8/src/thread/vita/SDL_syssem.c create mode 100644 contrib/SDL-3.2.8/src/thread/vita/SDL_systhread.c create mode 100644 contrib/SDL-3.2.8/src/thread/vita/SDL_systhread_c.h create mode 100644 contrib/SDL-3.2.8/src/thread/windows/SDL_syscond_cv.c create mode 100644 contrib/SDL-3.2.8/src/thread/windows/SDL_sysmutex.c create mode 100644 contrib/SDL-3.2.8/src/thread/windows/SDL_sysmutex_c.h create mode 100644 contrib/SDL-3.2.8/src/thread/windows/SDL_sysrwlock_srw.c create mode 100644 contrib/SDL-3.2.8/src/thread/windows/SDL_syssem.c create mode 100644 contrib/SDL-3.2.8/src/thread/windows/SDL_systhread.c create mode 100644 contrib/SDL-3.2.8/src/thread/windows/SDL_systhread_c.h create mode 100644 contrib/SDL-3.2.8/src/thread/windows/SDL_systls.c (limited to 'contrib/SDL-3.2.8/src/thread') diff --git a/contrib/SDL-3.2.8/src/thread/SDL_systhread.h b/contrib/SDL-3.2.8/src/thread/SDL_systhread.h new file mode 100644 index 0000000..596b248 --- /dev/null +++ b/contrib/SDL-3.2.8/src/thread/SDL_systhread.h @@ -0,0 +1,77 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + 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_ diff --git a/contrib/SDL-3.2.8/src/thread/SDL_thread.c b/contrib/SDL-3.2.8/src/thread/SDL_thread.c new file mode 100644 index 0000000..99cb9dd --- /dev/null +++ b/contrib/SDL-3.2.8/src/thread/SDL_thread.c @@ -0,0 +1,584 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + 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); + } +} + diff --git a/contrib/SDL-3.2.8/src/thread/SDL_thread_c.h b/contrib/SDL-3.2.8/src/thread/SDL_thread_c.h new file mode 100644 index 0000000..3368016 --- /dev/null +++ b/contrib/SDL-3.2.8/src/thread/SDL_thread_c.h @@ -0,0 +1,92 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + 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_ diff --git a/contrib/SDL-3.2.8/src/thread/generic/SDL_syscond.c b/contrib/SDL-3.2.8/src/thread/generic/SDL_syscond.c new file mode 100644 index 0000000..5fdfe84 --- /dev/null +++ b/contrib/SDL-3.2.8/src/thread/generic/SDL_syscond.c @@ -0,0 +1,216 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + 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 diff --git a/contrib/SDL-3.2.8/src/thread/generic/SDL_syscond_c.h b/contrib/SDL-3.2.8/src/thread/generic/SDL_syscond_c.h new file mode 100644 index 0000000..253c394 --- /dev/null +++ b/contrib/SDL-3.2.8/src/thread/generic/SDL_syscond_c.h @@ -0,0 +1,36 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + 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_ diff --git a/contrib/SDL-3.2.8/src/thread/generic/SDL_sysmutex.c b/contrib/SDL-3.2.8/src/thread/generic/SDL_sysmutex.c new file mode 100644 index 0000000..e58da67 --- /dev/null +++ b/contrib/SDL-3.2.8/src/thread/generic/SDL_sysmutex.c @@ -0,0 +1,132 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + 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 mutexes using semaphores + +#include "SDL_systhread_c.h" + +struct SDL_Mutex +{ + int recursive; + SDL_ThreadID owner; + SDL_Semaphore *sem; +}; + +SDL_Mutex *SDL_CreateMutex(void) +{ + SDL_Mutex *mutex = (SDL_Mutex *)SDL_calloc(1, sizeof(*mutex)); + +#ifndef SDL_THREADS_DISABLED + if (mutex) { + // Create the mutex semaphore, with initial value 1 + mutex->sem = SDL_CreateSemaphore(1); + mutex->recursive = 0; + mutex->owner = 0; + if (!mutex->sem) { + SDL_free(mutex); + mutex = NULL; + } + } +#endif // !SDL_THREADS_DISABLED + + return mutex; +} + +void SDL_DestroyMutex(SDL_Mutex *mutex) +{ + if (mutex) { + if (mutex->sem) { + SDL_DestroySemaphore(mutex->sem); + } + SDL_free(mutex); + } +} + +void SDL_LockMutex(SDL_Mutex *mutex) SDL_NO_THREAD_SAFETY_ANALYSIS // clang doesn't know about NULL mutexes +{ +#ifndef SDL_THREADS_DISABLED + if (mutex != NULL) { + SDL_ThreadID this_thread = SDL_GetCurrentThreadID(); + 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. + */ + SDL_WaitSemaphore(mutex->sem); + mutex->owner = this_thread; + mutex->recursive = 0; + } + } +#endif // SDL_THREADS_DISABLED +} + +bool SDL_TryLockMutex(SDL_Mutex *mutex) +{ + bool result = true; +#ifndef SDL_THREADS_DISABLED + if (mutex) { + SDL_ThreadID this_thread = SDL_GetCurrentThreadID(); + 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. + */ + result = SDL_TryWaitSemaphore(mutex->sem); + if (result) { + mutex->owner = this_thread; + mutex->recursive = 0; + } + } + } +#endif // SDL_THREADS_DISABLED + return result; +} + +void SDL_UnlockMutex(SDL_Mutex *mutex) SDL_NO_THREAD_SAFETY_ANALYSIS // clang doesn't know about NULL mutexes +{ +#ifndef SDL_THREADS_DISABLED + if (mutex != NULL) { + // If we don't own the mutex, we can't unlock it + if (SDL_GetCurrentThreadID() != mutex->owner) { + SDL_assert(!"Tried to unlock a mutex we don't own!"); + return; // (undefined behavior!) SDL_SetError("mutex not owned by this thread"); + } + + 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; + SDL_SignalSemaphore(mutex->sem); + } + } +#endif // SDL_THREADS_DISABLED +} + diff --git a/contrib/SDL-3.2.8/src/thread/generic/SDL_sysmutex_c.h b/contrib/SDL-3.2.8/src/thread/generic/SDL_sysmutex_c.h new file mode 100644 index 0000000..4b0c6f8 --- /dev/null +++ b/contrib/SDL-3.2.8/src/thread/generic/SDL_sysmutex_c.h @@ -0,0 +1,21 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + 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" diff --git a/contrib/SDL-3.2.8/src/thread/generic/SDL_sysrwlock.c b/contrib/SDL-3.2.8/src/thread/generic/SDL_sysrwlock.c new file mode 100644 index 0000000..24caf51 --- /dev/null +++ b/contrib/SDL-3.2.8/src/thread/generic/SDL_sysrwlock.c @@ -0,0 +1,194 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + 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 +} + diff --git a/contrib/SDL-3.2.8/src/thread/generic/SDL_sysrwlock_c.h b/contrib/SDL-3.2.8/src/thread/generic/SDL_sysrwlock_c.h new file mode 100644 index 0000000..9589d25 --- /dev/null +++ b/contrib/SDL-3.2.8/src/thread/generic/SDL_sysrwlock_c.h @@ -0,0 +1,38 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + 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_ diff --git a/contrib/SDL-3.2.8/src/thread/generic/SDL_syssem.c b/contrib/SDL-3.2.8/src/thread/generic/SDL_syssem.c new file mode 100644 index 0000000..dcd6fcc --- /dev/null +++ b/contrib/SDL-3.2.8/src/thread/generic/SDL_syssem.c @@ -0,0 +1,183 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + 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 diff --git a/contrib/SDL-3.2.8/src/thread/generic/SDL_systhread.c b/contrib/SDL-3.2.8/src/thread/generic/SDL_systhread.c new file mode 100644 index 0000000..ecfa4e1 --- /dev/null +++ b/contrib/SDL-3.2.8/src/thread/generic/SDL_systhread.c @@ -0,0 +1,57 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + 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" + +// Thread management routines for SDL + +#include "../SDL_systhread.h" + +bool SDL_SYS_CreateThread(SDL_Thread *thread, + SDL_FunctionPointer pfnBeginThread, + SDL_FunctionPointer pfnEndThread) +{ + return SDL_SetError("Threads are not supported on this platform"); +} + +void SDL_SYS_SetupThread(const char *name) +{ + return; +} + +SDL_ThreadID SDL_GetCurrentThreadID(void) +{ + return 0; +} + +bool SDL_SYS_SetThreadPriority(SDL_ThreadPriority priority) +{ + return true; +} + +void SDL_SYS_WaitThread(SDL_Thread *thread) +{ + return; +} + +void SDL_SYS_DetachThread(SDL_Thread *thread) +{ + return; +} diff --git a/contrib/SDL-3.2.8/src/thread/generic/SDL_systhread_c.h b/contrib/SDL-3.2.8/src/thread/generic/SDL_systhread_c.h new file mode 100644 index 0000000..9ba55fd --- /dev/null +++ b/contrib/SDL-3.2.8/src/thread/generic/SDL_systhread_c.h @@ -0,0 +1,24 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + 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; diff --git a/contrib/SDL-3.2.8/src/thread/generic/SDL_systls.c b/contrib/SDL-3.2.8/src/thread/generic/SDL_systls.c new file mode 100644 index 0000000..b8ebafe --- /dev/null +++ b/contrib/SDL-3.2.8/src/thread/generic/SDL_systls.c @@ -0,0 +1,44 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + 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_thread_c.h" + +void SDL_SYS_InitTLSData(void) +{ + SDL_Generic_InitTLSData(); +} + +SDL_TLSData *SDL_SYS_GetTLSData(void) +{ + return SDL_Generic_GetTLSData(); +} + +bool SDL_SYS_SetTLSData(SDL_TLSData *data) +{ + return SDL_Generic_SetTLSData(data); +} + +void SDL_SYS_QuitTLSData(void) +{ + SDL_Generic_QuitTLSData(); +} + diff --git a/contrib/SDL-3.2.8/src/thread/n3ds/SDL_sysmutex.c b/contrib/SDL-3.2.8/src/thread/n3ds/SDL_sysmutex.c new file mode 100644 index 0000000..0abb9f5 --- /dev/null +++ b/contrib/SDL-3.2.8/src/thread/n3ds/SDL_sysmutex.c @@ -0,0 +1,67 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + 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_N3DS + +// An implementation of mutexes using libctru's RecursiveLock + +#include "SDL_sysmutex_c.h" + +SDL_Mutex *SDL_CreateMutex(void) +{ + SDL_Mutex *mutex = (SDL_Mutex *)SDL_malloc(sizeof(*mutex)); + if (mutex) { + RecursiveLock_Init(&mutex->lock); + } + return mutex; +} + +void SDL_DestroyMutex(SDL_Mutex *mutex) +{ + if (mutex) { + SDL_free(mutex); + } +} + +void SDL_LockMutex(SDL_Mutex *mutex) SDL_NO_THREAD_SAFETY_ANALYSIS // clang doesn't know about NULL mutexes +{ + if (mutex) { + RecursiveLock_Lock(&mutex->lock); + } +} + +bool SDL_TryLockMutex(SDL_Mutex *mutex) +{ + if (mutex) { + return RecursiveLock_TryLock(&mutex->lock); + } + return true; +} + +void SDL_UnlockMutex(SDL_Mutex *mutex) SDL_NO_THREAD_SAFETY_ANALYSIS // clang doesn't know about NULL mutexes +{ + if (mutex) { + RecursiveLock_Unlock(&mutex->lock); + } +} + +#endif // SDL_THREAD_N3DS diff --git a/contrib/SDL-3.2.8/src/thread/n3ds/SDL_sysmutex_c.h b/contrib/SDL-3.2.8/src/thread/n3ds/SDL_sysmutex_c.h new file mode 100644 index 0000000..ddf8069 --- /dev/null +++ b/contrib/SDL-3.2.8/src/thread/n3ds/SDL_sysmutex_c.h @@ -0,0 +1,33 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + 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_sysmutex_c_h_ +#define SDL_sysmutex_c_h_ + +#include <3ds.h> + +struct SDL_Mutex +{ + RecursiveLock lock; +}; + +#endif // SDL_sysmutex_c_h diff --git a/contrib/SDL-3.2.8/src/thread/n3ds/SDL_syssem.c b/contrib/SDL-3.2.8/src/thread/n3ds/SDL_syssem.c new file mode 100644 index 0000000..3207a2d --- /dev/null +++ b/contrib/SDL-3.2.8/src/thread/n3ds/SDL_syssem.c @@ -0,0 +1,112 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + 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_N3DS + +// An implementation of semaphores using libctru's LightSemaphore + +#include <3ds.h> + +struct SDL_Semaphore +{ + LightSemaphore semaphore; +}; + +SDL_Semaphore *SDL_CreateSemaphore(Uint32 initial_value) +{ + SDL_Semaphore *sem; + + if (initial_value > SDL_MAX_SINT16) { + SDL_SetError("Initial semaphore value too high for this platform"); + return NULL; + } + + sem = (SDL_Semaphore *)SDL_malloc(sizeof(*sem)); + if (!sem) { + return NULL; + } + + LightSemaphore_Init(&sem->semaphore, initial_value, SDL_MAX_SINT16); + + return sem; +} + +/* WARNING: + You cannot call this function when another thread is using the semaphore. +*/ +void SDL_DestroySemaphore(SDL_Semaphore *sem) +{ + SDL_free(sem); +} + +static bool WaitOnSemaphoreFor(SDL_Semaphore *sem, Sint64 timeoutNS) +{ + Uint64 stop_time = SDL_GetTicksNS() + timeoutNS; + while (SDL_GetTicksNS() < stop_time) { + if (LightSemaphore_TryAcquire(&sem->semaphore, 1) == 0) { + return true; + } + // 100 microseconds seems to be the sweet spot + SDL_DelayNS(SDL_US_TO_NS(100)); + } + + // If we failed, yield to avoid starvation on busy waits + SDL_DelayNS(1); + return false; +} + +bool SDL_WaitSemaphoreTimeoutNS(SDL_Semaphore *sem, Sint64 timeoutNS) +{ + if (!sem) { + return true; + } + + if (timeoutNS < 0) { // -1 == wait indefinitely. + LightSemaphore_Acquire(&sem->semaphore, 1); + return true; + } + + if (LightSemaphore_TryAcquire(&sem->semaphore, 1) == 0) { + return true; + } + + return WaitOnSemaphoreFor(sem, timeoutNS); +} + +Uint32 SDL_GetSemaphoreValue(SDL_Semaphore *sem) +{ + if (!sem) { + return 0; + } + return sem->semaphore.current_count; +} + +void SDL_SignalSemaphore(SDL_Semaphore *sem) +{ + if (sem) { + return; + } + + LightSemaphore_Release(&sem->semaphore, 1); +} + +#endif // SDL_THREAD_N3DS diff --git a/contrib/SDL-3.2.8/src/thread/n3ds/SDL_systhread.c b/contrib/SDL-3.2.8/src/thread/n3ds/SDL_systhread.c new file mode 100644 index 0000000..6bd826b --- /dev/null +++ b/contrib/SDL-3.2.8/src/thread/n3ds/SDL_systhread.c @@ -0,0 +1,139 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + 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_N3DS + +// Thread management routines for SDL + +#include "../SDL_systhread.h" + +// N3DS has very limited RAM (128MB), so we set a low default thread stack size. +#define N3DS_THREAD_STACK_SIZE_DEFAULT (80 * 1024) + +#define N3DS_THREAD_PRIORITY_LOW 0x3F /**< Minimum priority */ +#define N3DS_THREAD_PRIORITY_MEDIUM 0x2F /**< Slightly higher than main thread (0x30) */ +#define N3DS_THREAD_PRIORITY_HIGH 0x19 /**< High priority for non-video work */ +#define N3DS_THREAD_PRIORITY_TIME_CRITICAL 0x18 /**< Highest priority */ + +static size_t GetStackSize(size_t requested_size); + +static void ThreadEntry(void *arg) +{ + SDL_RunThread((SDL_Thread *)arg); + threadExit(0); +} + + +bool SDL_SYS_CreateThread(SDL_Thread *thread, + SDL_FunctionPointer pfnBeginThread, + SDL_FunctionPointer pfnEndThread) +{ + s32 priority = 0x30; + int cpu = -1; + size_t stack_size = GetStackSize(thread->stacksize); + + svcGetThreadPriority(&priority, CUR_THREAD_HANDLE); + + // prefer putting audio thread on system core + if (thread->name && (SDL_strncmp(thread->name, "SDLAudioP", 9) == 0) && R_SUCCEEDED(APT_SetAppCpuTimeLimit(30))) { + cpu = 1; + } + + thread->handle = threadCreate(ThreadEntry, + thread, + stack_size, + priority, + cpu, + false); + + if (!thread->handle) { + return SDL_SetError("Couldn't create thread"); + } + + return true; +} + +static size_t GetStackSize(size_t requested_size) +{ + if (requested_size == 0) { + return N3DS_THREAD_STACK_SIZE_DEFAULT; + } + + return requested_size; +} + +void SDL_SYS_SetupThread(const char *name) +{ + return; +} + +SDL_ThreadID SDL_GetCurrentThreadID(void) +{ + u32 thread_ID = 0; + svcGetThreadId(&thread_ID, CUR_THREAD_HANDLE); + return (SDL_ThreadID)thread_ID; +} + +bool SDL_SYS_SetThreadPriority(SDL_ThreadPriority sdl_priority) +{ + s32 svc_priority; + switch (sdl_priority) { + case SDL_THREAD_PRIORITY_LOW: + svc_priority = N3DS_THREAD_PRIORITY_LOW; + break; + case SDL_THREAD_PRIORITY_NORMAL: + svc_priority = N3DS_THREAD_PRIORITY_MEDIUM; + break; + case SDL_THREAD_PRIORITY_HIGH: + svc_priority = N3DS_THREAD_PRIORITY_HIGH; + break; + case SDL_THREAD_PRIORITY_TIME_CRITICAL: + svc_priority = N3DS_THREAD_PRIORITY_TIME_CRITICAL; + break; + default: + svc_priority = N3DS_THREAD_PRIORITY_MEDIUM; + } + if (svcSetThreadPriority(CUR_THREAD_HANDLE, svc_priority) < 0) { + return SDL_SetError("svcSetThreadPriority failed"); + } + return true; +} + +void SDL_SYS_WaitThread(SDL_Thread *thread) +{ + Result res = threadJoin(thread->handle, U64_MAX); + + /* + Detached threads can be waited on, but should NOT be cleaned manually + as it would result in a fatal error. + */ + if (R_SUCCEEDED(res) && SDL_GetThreadState(thread) != SDL_THREAD_DETACHED) { + threadFree(thread->handle); + } +} + +void SDL_SYS_DetachThread(SDL_Thread *thread) +{ + threadDetach(thread->handle); +} + +#endif // SDL_THREAD_N3DS diff --git a/contrib/SDL-3.2.8/src/thread/n3ds/SDL_systhread_c.h b/contrib/SDL-3.2.8/src/thread/n3ds/SDL_systhread_c.h new file mode 100644 index 0000000..d87d636 --- /dev/null +++ b/contrib/SDL-3.2.8/src/thread/n3ds/SDL_systhread_c.h @@ -0,0 +1,30 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + 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 <3ds.h> + +typedef Thread SYS_ThreadHandle; + +#endif // SDL_systhread_c_h_ diff --git a/contrib/SDL-3.2.8/src/thread/ps2/SDL_syssem.c b/contrib/SDL-3.2.8/src/thread/ps2/SDL_syssem.c new file mode 100644 index 0000000..ccd09c0 --- /dev/null +++ b/contrib/SDL-3.2.8/src/thread/ps2/SDL_syssem.c @@ -0,0 +1,122 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + 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_PS2 + +// Semaphore functions for the PS2. + +#include +#include +#include + +#include + +struct SDL_Semaphore +{ + s32 semid; +}; + +// Create a semaphore +SDL_Semaphore *SDL_CreateSemaphore(Uint32 initial_value) +{ + SDL_Semaphore *sem; + ee_sema_t sema; + + sem = (SDL_Semaphore *)SDL_malloc(sizeof(*sem)); + if (sem) { + // TODO: Figure out the limit on the maximum value. + sema.init_count = initial_value; + sema.max_count = 255; + sema.option = 0; + sem->semid = CreateSema(&sema); + + if (sem->semid < 0) { + SDL_SetError("Couldn't create semaphore"); + SDL_free(sem); + sem = NULL; + } + } + + return sem; +} + +// Free the semaphore +void SDL_DestroySemaphore(SDL_Semaphore *sem) +{ + if (sem) { + if (sem->semid > 0) { + DeleteSema(sem->semid); + sem->semid = 0; + } + + SDL_free(sem); + } +} + +bool SDL_WaitSemaphoreTimeoutNS(SDL_Semaphore *sem, Sint64 timeoutNS) +{ + u64 timeout_usec; + u64 *timeout_ptr; + + if (!sem) { + return true; + } + + if (timeoutNS == 0) { + return (PollSema(sem->semid) == 0); + } + + timeout_ptr = NULL; + + if (timeoutNS != -1) { // -1 == wait indefinitely. + timeout_usec = SDL_NS_TO_US(timeoutNS); + timeout_ptr = &timeout_usec; + } + + return (WaitSemaEx(sem->semid, 1, timeout_ptr) == 0); +} + +// Returns the current count of the semaphore +Uint32 SDL_GetSemaphoreValue(SDL_Semaphore *sem) +{ + ee_sema_t info; + + if (!sem) { + return 0; + } + + if (ReferSemaStatus(sem->semid, &info) == 0) { + return info.count; + } + return 0; +} + +void SDL_SignalSemaphore(SDL_Semaphore *sem) +{ + if (!sem) { + return; + } + + SignalSema(sem->semid); +} + +#endif // SDL_THREAD_PS2 diff --git a/contrib/SDL-3.2.8/src/thread/ps2/SDL_systhread.c b/contrib/SDL-3.2.8/src/thread/ps2/SDL_systhread.c new file mode 100644 index 0000000..a13da42 --- /dev/null +++ b/contrib/SDL-3.2.8/src/thread/ps2/SDL_systhread.c @@ -0,0 +1,143 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + 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_PS2 + +// PS2 thread management routines for SDL + +#include +#include + +#include "../SDL_systhread.h" +#include "../SDL_thread_c.h" +#include + +static void FinishThread(SDL_Thread *thread) +{ + ee_thread_status_t info; + int res; + + res = ReferThreadStatus(thread->handle, &info); + TerminateThread(thread->handle); + DeleteThread(thread->handle); + DeleteSema((int)thread->endfunc); + + if (res > 0) { + SDL_free(info.stack); + } +} + +static int childThread(void *arg) +{ + SDL_Thread *thread = (SDL_Thread *)arg; + int res = thread->userfunc(thread->userdata); + SignalSema((int)thread->endfunc); + return res; +} + +bool SDL_SYS_CreateThread(SDL_Thread *thread, + SDL_FunctionPointer pfnBeginThread, + SDL_FunctionPointer pfnEndThread) +{ + ee_thread_status_t status; + ee_thread_t eethread; + ee_sema_t sema; + size_t stack_size; + int priority = 32; + + // Set priority of new thread to the same as the current thread + // status.size = sizeof(ee_thread_t); + if (ReferThreadStatus(GetThreadId(), &status) == 0) { + priority = status.current_priority; + } + + stack_size = thread->stacksize ? ((int)thread->stacksize) : 0x1800; + + // Create EE Thread + eethread.attr = 0; + eethread.option = 0; + eethread.func = &childThread; + eethread.stack = SDL_malloc(stack_size); + eethread.stack_size = stack_size; + eethread.gp_reg = &_gp; + eethread.initial_priority = priority; + thread->handle = CreateThread(&eethread); + + if (thread->handle < 0) { + return SDL_SetError("CreateThread() failed"); + } + + // Prepare el semaphore for the ending function + sema.init_count = 0; + sema.max_count = 1; + sema.option = 0; + thread->endfunc = (void *)CreateSema(&sema); + + if (StartThread(thread->handle, thread) < 0) { + return SDL_SetError("StartThread() failed"); + } + return true; +} + +void SDL_SYS_SetupThread(const char *name) +{ + // Do nothing. +} + +SDL_ThreadID SDL_GetCurrentThreadID(void) +{ + return (SDL_ThreadID)GetThreadId(); +} + +void SDL_SYS_WaitThread(SDL_Thread *thread) +{ + WaitSema((int)thread->endfunc); + ReleaseWaitThread(thread->handle); + FinishThread(thread); +} + +void SDL_SYS_DetachThread(SDL_Thread *thread) +{ + // Do nothing. +} + +bool SDL_SYS_SetThreadPriority(SDL_ThreadPriority priority) +{ + int value; + + if (priority == SDL_THREAD_PRIORITY_LOW) { + value = 111; + } else if (priority == SDL_THREAD_PRIORITY_HIGH) { + value = 32; + } else if (priority == SDL_THREAD_PRIORITY_TIME_CRITICAL) { + value = 16; + } else { + value = 50; + } + + if (ChangeThreadPriority(GetThreadId(), value) < 0) { + return SDL_SetError("ChangeThreadPriority() failed"); + } + return true; +} + +#endif // SDL_THREAD_PS2 diff --git a/contrib/SDL-3.2.8/src/thread/ps2/SDL_systhread_c.h b/contrib/SDL-3.2.8/src/thread/ps2/SDL_systhread_c.h new file mode 100644 index 0000000..2a811ce --- /dev/null +++ b/contrib/SDL-3.2.8/src/thread/ps2/SDL_systhread_c.h @@ -0,0 +1,22 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + 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. +*/ + +typedef int32_t SYS_ThreadHandle; diff --git a/contrib/SDL-3.2.8/src/thread/psp/SDL_sysmutex.c b/contrib/SDL-3.2.8/src/thread/psp/SDL_sysmutex.c new file mode 100644 index 0000000..c8ed80d --- /dev/null +++ b/contrib/SDL-3.2.8/src/thread/psp/SDL_sysmutex.c @@ -0,0 +1,100 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + 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_PSP + +// An implementation of mutexes using semaphores + +#include "SDL_systhread_c.h" + +#include +#include + +#define SCE_KERNEL_MUTEX_ATTR_RECURSIVE 0x0200U + +struct SDL_Mutex +{ + SceLwMutexWorkarea lock; +}; + +SDL_Mutex *SDL_CreateMutex(void) +{ + SDL_Mutex *mutex = (SDL_Mutex *)SDL_malloc(sizeof(*mutex)); + if (mutex) { + const SceInt32 res = sceKernelCreateLwMutex( + &mutex->lock, + "SDL mutex", + SCE_KERNEL_MUTEX_ATTR_RECURSIVE, + 0, + NULL); + + if (res < 0) { + SDL_free(mutex); + mutex = NULL; + SDL_SetError("Error trying to create mutex: %lx", res); + } + } + return mutex; +} + +void SDL_DestroyMutex(SDL_Mutex *mutex) +{ + if (mutex) { + sceKernelDeleteLwMutex(&mutex->lock); + SDL_free(mutex); + } +} + +void SDL_LockMutex(SDL_Mutex *mutex) SDL_NO_THREAD_SAFETY_ANALYSIS // clang doesn't know about NULL mutexes +{ + if (mutex) { + const SceInt32 res = sceKernelLockLwMutex(&mutex->lock, 1, NULL); + SDL_assert(res == SCE_KERNEL_ERROR_OK); // assume we're in a lot of trouble if this assert fails. + } +} + +bool SDL_TryLockMutex(SDL_Mutex *mutex) +{ + bool result = true; + if (mutex) { + const SceInt32 res = sceKernelTryLockLwMutex(&mutex->lock, 1); + if (res == SCE_KERNEL_ERROR_OK) { + result = true; + } else if (res == SCE_KERNEL_ERROR_WAIT_TIMEOUT) { + result = false; + } else { + SDL_assert(res == SCE_KERNEL_ERROR_OK); // assume we're in a lot of trouble if this assert fails. + result = false; + } + } + return result; +} + +void SDL_UnlockMutex(SDL_Mutex *mutex) SDL_NO_THREAD_SAFETY_ANALYSIS // clang doesn't know about NULL mutexes +{ + if (mutex) { + const SceInt32 res = sceKernelUnlockLwMutex(&mutex->lock, 1); + SDL_assert(res == SCE_KERNEL_ERROR_OK); // assume we're in a lot of trouble if this assert fails. + } +} + +#endif // SDL_THREAD_PSP diff --git a/contrib/SDL-3.2.8/src/thread/psp/SDL_sysmutex_c.h b/contrib/SDL-3.2.8/src/thread/psp/SDL_sysmutex_c.h new file mode 100644 index 0000000..4b0c6f8 --- /dev/null +++ b/contrib/SDL-3.2.8/src/thread/psp/SDL_sysmutex_c.h @@ -0,0 +1,21 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + 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" diff --git a/contrib/SDL-3.2.8/src/thread/psp/SDL_syssem.c b/contrib/SDL-3.2.8/src/thread/psp/SDL_syssem.c new file mode 100644 index 0000000..27e332f --- /dev/null +++ b/contrib/SDL-3.2.8/src/thread/psp/SDL_syssem.c @@ -0,0 +1,119 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + 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_PSP + +// Semaphore functions for the PSP. + +#include +#include + +#include +#include + +struct SDL_Semaphore +{ + SceUID semid; +}; + +// Create a semaphore +SDL_Semaphore *SDL_CreateSemaphore(Uint32 initial_value) +{ + SDL_Semaphore *sem; + + sem = (SDL_Semaphore *)SDL_malloc(sizeof(*sem)); + if (sem) { + // TODO: Figure out the limit on the maximum value. + sem->semid = sceKernelCreateSema("SDL sema", 0, initial_value, 255, NULL); + if (sem->semid < 0) { + SDL_SetError("Couldn't create semaphore"); + SDL_free(sem); + sem = NULL; + } + } + + return sem; +} + +// Free the semaphore +void SDL_DestroySemaphore(SDL_Semaphore *sem) +{ + if (sem) { + if (sem->semid > 0) { + sceKernelDeleteSema(sem->semid); + sem->semid = 0; + } + + SDL_free(sem); + } +} + +/* TODO: This routine is a bit overloaded. + * If the timeout is 0 then just poll the semaphore; if it's -1, pass + * NULL to sceKernelWaitSema() so that it waits indefinitely; and if the timeout + * is specified, convert it to microseconds. */ +bool SDL_WaitSemaphoreTimeoutNS(SDL_Semaphore *sem, Sint64 timeoutNS) +{ + SceUInt timeoutUS; + SceUInt *pTimeout = NULL; + + if (!sem) { + return true; + } + + if (timeoutNS == 0) { + return (sceKernelPollSema(sem->semid, 1) == 0); + } + + if (timeoutNS > 0) { + timeoutUS = (SceUInt)SDL_NS_TO_US(timeoutNS); // Convert to microseconds. + pTimeout = &timeoutUS; + } + + return (sceKernelWaitSema(sem->semid, 1, pTimeout) == 0); +} + +// Returns the current count of the semaphore +Uint32 SDL_GetSemaphoreValue(SDL_Semaphore *sem) +{ + SceKernelSemaInfo info; + + if (!sem) { + return 0; + } + + if (sceKernelReferSemaStatus(sem->semid, &info) == 0) { + return info.currentCount; + } + return 0; +} + +void SDL_SignalSemaphore(SDL_Semaphore *sem) +{ + if (!sem) { + return; + } + + sceKernelSignalSema(sem->semid, 1); +} + +#endif // SDL_THREAD_PSP diff --git a/contrib/SDL-3.2.8/src/thread/psp/SDL_systhread.c b/contrib/SDL-3.2.8/src/thread/psp/SDL_systhread.c new file mode 100644 index 0000000..3d60718 --- /dev/null +++ b/contrib/SDL-3.2.8/src/thread/psp/SDL_systhread.c @@ -0,0 +1,112 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + 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_PSP + +// PSP thread management routines for SDL + +#include +#include + +#include "../SDL_systhread.h" +#include "../SDL_thread_c.h" +#include +#include + +static int ThreadEntry(SceSize args, void *argp) +{ + SDL_RunThread(*(SDL_Thread **)argp); + return 0; +} + +bool SDL_SYS_CreateThread(SDL_Thread *thread, + SDL_FunctionPointer pfnBeginThread, + SDL_FunctionPointer pfnEndThread) +{ + SceKernelThreadInfo status; + int priority = 32; + + // Set priority of new thread to the same as the current thread + status.size = sizeof(SceKernelThreadInfo); + if (sceKernelReferThreadStatus(sceKernelGetThreadId(), &status) == 0) { + priority = status.currentPriority; + } + + thread->handle = sceKernelCreateThread(thread->name, ThreadEntry, + priority, thread->stacksize ? ((int)thread->stacksize) : 0x8000, + PSP_THREAD_ATTR_VFPU, NULL); + if (thread->handle < 0) { + return SDL_SetError("sceKernelCreateThread() failed"); + } + + sceKernelStartThread(thread->handle, 4, &thread); + return true; +} + +void SDL_SYS_SetupThread(const char *name) +{ + // Do nothing. +} + +SDL_ThreadID SDL_GetCurrentThreadID(void) +{ + return (SDL_ThreadID)sceKernelGetThreadId(); +} + +void SDL_SYS_WaitThread(SDL_Thread *thread) +{ + sceKernelWaitThreadEnd(thread->handle, NULL); + sceKernelDeleteThread(thread->handle); +} + +void SDL_SYS_DetachThread(SDL_Thread *thread) +{ + // !!! FIXME: is this correct? + sceKernelDeleteThread(thread->handle); +} + +void SDL_SYS_KillThread(SDL_Thread *thread) +{ + sceKernelTerminateDeleteThread(thread->handle); +} + +bool SDL_SYS_SetThreadPriority(SDL_ThreadPriority priority) +{ + int value; + + if (priority == SDL_THREAD_PRIORITY_LOW) { + value = 111; + } else if (priority == SDL_THREAD_PRIORITY_HIGH) { + value = 32; + } else if (priority == SDL_THREAD_PRIORITY_TIME_CRITICAL) { + value = 16; + } else { + value = 50; + } + + if (sceKernelChangeThreadPriority(sceKernelGetThreadId(), value) < 0) { + return SDL_SetError("sceKernelChangeThreadPriority() failed"); + } + return true; +} + +#endif // SDL_THREAD_PSP diff --git a/contrib/SDL-3.2.8/src/thread/psp/SDL_systhread_c.h b/contrib/SDL-3.2.8/src/thread/psp/SDL_systhread_c.h new file mode 100644 index 0000000..9e951ec --- /dev/null +++ b/contrib/SDL-3.2.8/src/thread/psp/SDL_systhread_c.h @@ -0,0 +1,24 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + 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 + +typedef SceUID SYS_ThreadHandle; diff --git a/contrib/SDL-3.2.8/src/thread/pthread/SDL_syscond.c b/contrib/SDL-3.2.8/src/thread/pthread/SDL_syscond.c new file mode 100644 index 0000000..c3e4098 --- /dev/null +++ b/contrib/SDL-3.2.8/src/thread/pthread/SDL_syscond.c @@ -0,0 +1,128 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + 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 +#include +#include +#include +#include + +#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; +} diff --git a/contrib/SDL-3.2.8/src/thread/pthread/SDL_sysmutex.c b/contrib/SDL-3.2.8/src/thread/pthread/SDL_sysmutex.c new file mode 100644 index 0000000..7bba286 --- /dev/null +++ b/contrib/SDL-3.2.8/src/thread/pthread/SDL_sysmutex.c @@ -0,0 +1,154 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + 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 +#include + +#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 + } +} + diff --git a/contrib/SDL-3.2.8/src/thread/pthread/SDL_sysmutex_c.h b/contrib/SDL-3.2.8/src/thread/pthread/SDL_sysmutex_c.h new file mode 100644 index 0000000..f69e7b3 --- /dev/null +++ b/contrib/SDL-3.2.8/src/thread/pthread/SDL_sysmutex_c.h @@ -0,0 +1,40 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + 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_ diff --git a/contrib/SDL-3.2.8/src/thread/pthread/SDL_sysrwlock.c b/contrib/SDL-3.2.8/src/thread/pthread/SDL_sysrwlock.c new file mode 100644 index 0000000..3cf02e5 --- /dev/null +++ b/contrib/SDL-3.2.8/src/thread/pthread/SDL_sysrwlock.c @@ -0,0 +1,113 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + 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 +#include + +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. + } +} + diff --git a/contrib/SDL-3.2.8/src/thread/pthread/SDL_syssem.c b/contrib/SDL-3.2.8/src/thread/pthread/SDL_syssem.c new file mode 100644 index 0000000..d5d3298 --- /dev/null +++ b/contrib/SDL-3.2.8/src/thread/pthread/SDL_syssem.c @@ -0,0 +1,160 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + 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 +#include +#include +#include +#include + +// 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 diff --git a/contrib/SDL-3.2.8/src/thread/pthread/SDL_systhread.c b/contrib/SDL-3.2.8/src/thread/pthread/SDL_systhread.c new file mode 100644 index 0000000..ae4a94c --- /dev/null +++ b/contrib/SDL-3.2.8/src/thread/pthread/SDL_systhread.c @@ -0,0 +1,293 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + 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 + +#ifdef HAVE_PTHREAD_NP_H +#include +#endif + +#ifdef HAVE_SIGNAL_H +#include +#endif +#include + +#ifdef SDL_PLATFORM_LINUX +#include +#include +#include +#include + +#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 +#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 +#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); +} diff --git a/contrib/SDL-3.2.8/src/thread/pthread/SDL_systhread_c.h b/contrib/SDL-3.2.8/src/thread/pthread/SDL_systhread_c.h new file mode 100644 index 0000000..bdfcd68 --- /dev/null +++ b/contrib/SDL-3.2.8/src/thread/pthread/SDL_systhread_c.h @@ -0,0 +1,25 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + 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 + +typedef pthread_t SYS_ThreadHandle; diff --git a/contrib/SDL-3.2.8/src/thread/pthread/SDL_systls.c b/contrib/SDL-3.2.8/src/thread/pthread/SDL_systls.c new file mode 100644 index 0000000..18e9a01 --- /dev/null +++ b/contrib/SDL-3.2.8/src/thread/pthread/SDL_systls.c @@ -0,0 +1,78 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + 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 + +#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; + } + } +} diff --git a/contrib/SDL-3.2.8/src/thread/vita/SDL_sysmutex.c b/contrib/SDL-3.2.8/src/thread/vita/SDL_sysmutex.c new file mode 100644 index 0000000..8a3664c --- /dev/null +++ b/contrib/SDL-3.2.8/src/thread/vita/SDL_sysmutex.c @@ -0,0 +1,97 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + 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_VITA + +#include "SDL_systhread_c.h" + +#include +#include + +struct SDL_Mutex +{ + SceKernelLwMutexWork lock; +}; + +SDL_Mutex *SDL_CreateMutex(void) +{ + SDL_Mutex *mutex = (SDL_Mutex *)SDL_malloc(sizeof(*mutex)); + if (mutex) { + const SceInt32 res = sceKernelCreateLwMutex( + &mutex->lock, + "SDL mutex", + SCE_KERNEL_MUTEX_ATTR_RECURSIVE, + 0, + NULL); + + if (res < 0) { + SDL_free(mutex); + mutex = NULL; + SDL_SetError("Error trying to create mutex: %x", res); + } + } + return mutex; +} + +void SDL_DestroyMutex(SDL_Mutex *mutex) +{ + if (mutex) { + sceKernelDeleteLwMutex(&mutex->lock); + SDL_free(mutex); + } +} + +void SDL_LockMutex(SDL_Mutex *mutex) SDL_NO_THREAD_SAFETY_ANALYSIS // clang doesn't know about NULL mutexes +{ + if (mutex) { + const SceInt32 res = sceKernelLockLwMutex(&mutex->lock, 1, NULL); + SDL_assert(res == SCE_KERNEL_OK); // assume we're in a lot of trouble if this assert fails. + } +} + +bool SDL_TryLockMutex(SDL_Mutex *mutex) +{ + bool result = true; + + if (mutex) { + const SceInt32 res = sceKernelTryLockLwMutex(&mutex->lock, 1); + if (res == SCE_KERNEL_OK) { + result = true; + } else if (res == SCE_KERNEL_ERROR_MUTEX_FAILED_TO_OWN) { + result = false; + } else { + SDL_assert(res == SCE_KERNEL_OK); // assume we're in a lot of trouble if this assert fails. + result = false; + } + } + return result; +} + +void SDL_UnlockMutex(SDL_Mutex *mutex) SDL_NO_THREAD_SAFETY_ANALYSIS // clang doesn't know about NULL mutexes +{ + if (mutex) { + const SceInt32 res = sceKernelUnlockLwMutex(&mutex->lock, 1); + SDL_assert(res == SCE_KERNEL_OK); // assume we're in a lot of trouble if this assert fails. + } +} + +#endif // SDL_THREAD_VITA diff --git a/contrib/SDL-3.2.8/src/thread/vita/SDL_sysmutex_c.h b/contrib/SDL-3.2.8/src/thread/vita/SDL_sysmutex_c.h new file mode 100644 index 0000000..4b0c6f8 --- /dev/null +++ b/contrib/SDL-3.2.8/src/thread/vita/SDL_sysmutex_c.h @@ -0,0 +1,21 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + 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" diff --git a/contrib/SDL-3.2.8/src/thread/vita/SDL_syssem.c b/contrib/SDL-3.2.8/src/thread/vita/SDL_syssem.c new file mode 100644 index 0000000..3c6d28a --- /dev/null +++ b/contrib/SDL-3.2.8/src/thread/vita/SDL_syssem.c @@ -0,0 +1,122 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + 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_VITA + +// Semaphore functions for the VITA. + +#include +#include + +#include +#include +#include + +struct SDL_Semaphore +{ + SceUID semid; +}; + +// Create a semaphore +SDL_Semaphore *SDL_CreateSemaphore(Uint32 initial_value) +{ + SDL_Semaphore *sem; + + sem = (SDL_Semaphore *)SDL_malloc(sizeof(*sem)); + if (sem) { + // TODO: Figure out the limit on the maximum value. + sem->semid = sceKernelCreateSema("SDL sema", 0, initial_value, 255, NULL); + if (sem->semid < 0) { + SDL_SetError("Couldn't create semaphore"); + SDL_free(sem); + sem = NULL; + } + } + + return sem; +} + +// Free the semaphore +void SDL_DestroySemaphore(SDL_Semaphore *sem) +{ + if (sem) { + if (sem->semid > 0) { + sceKernelDeleteSema(sem->semid); + sem->semid = 0; + } + + SDL_free(sem); + } +} + +/* TODO: This routine is a bit overloaded. + * If the timeout is 0 then just poll the semaphore; if it's -1, pass + * NULL to sceKernelWaitSema() so that it waits indefinitely; and if the timeout + * is specified, convert it to microseconds. */ +bool SDL_WaitSemaphoreTimeoutNS(SDL_Semaphore *sem, Sint64 timeoutNS) +{ + SceUInt timeoutUS; + SceUInt *pTimeout = NULL; + + if (!sem) { + return true; + } + + if (timeoutNS == 0) { + return (sceKernelPollSema(sem->semid, 1) == 0); + } + + if (timeoutNS > 0) { + timeoutUS = (SceUInt)SDL_NS_TO_US(timeoutNS); // Convert to microseconds. + pTimeout = &timeoutUS; + } + + return (sceKernelWaitSema(sem->semid, 1, pTimeout) == 0); +} + +// Returns the current count of the semaphore +Uint32 SDL_GetSemaphoreValue(SDL_Semaphore *sem) +{ + SceKernelSemaInfo info; + info.size = sizeof(info); + + if (!sem) { + return 0; + } + + if (sceKernelGetSemaInfo(sem->semid, &info) >= 0) { + return info.currentCount; + } + + return 0; +} + +void SDL_SignalSemaphore(SDL_Semaphore *sem) +{ + if (!sem) { + return; + } + + sceKernelSignalSema(sem->semid, 1); +} + +#endif // SDL_THREAD_VITA diff --git a/contrib/SDL-3.2.8/src/thread/vita/SDL_systhread.c b/contrib/SDL-3.2.8/src/thread/vita/SDL_systhread.c new file mode 100644 index 0000000..344d911 --- /dev/null +++ b/contrib/SDL-3.2.8/src/thread/vita/SDL_systhread.c @@ -0,0 +1,139 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + 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_VITA + +// VITA thread management routines for SDL + +#include +#include + +#include "../SDL_systhread.h" +#include "../SDL_thread_c.h" +#include +#include + +#define VITA_THREAD_STACK_SIZE_MIN 0x1000 // 4KiB +#define VITA_THREAD_STACK_SIZE_MAX 0x2000000 // 32MiB +#define VITA_THREAD_STACK_SIZE_DEFAULT 0x10000 // 64KiB +#define VITA_THREAD_NAME_MAX 32 + +#define VITA_THREAD_PRIORITY_LOW 191 +#define VITA_THREAD_PRIORITY_NORMAL 160 +#define VITA_THREAD_PRIORITY_HIGH 112 +#define VITA_THREAD_PRIORITY_TIME_CRITICAL 64 + +static int ThreadEntry(SceSize args, void *argp) +{ + SDL_RunThread(*(SDL_Thread **)argp); + return 0; +} + +bool SDL_SYS_CreateThread(SDL_Thread *thread, + SDL_FunctionPointer pfnBeginThread, + SDL_FunctionPointer pfnEndThread) + +{ + char thread_name[VITA_THREAD_NAME_MAX]; + size_t stack_size = VITA_THREAD_STACK_SIZE_DEFAULT; + + SDL_strlcpy(thread_name, "SDL thread", VITA_THREAD_NAME_MAX); + if (thread->name) { + SDL_strlcpy(thread_name, thread->name, VITA_THREAD_NAME_MAX); + } + + if (thread->stacksize) { + if (thread->stacksize < VITA_THREAD_STACK_SIZE_MIN) { + thread->stacksize = VITA_THREAD_STACK_SIZE_MIN; + } + if (thread->stacksize > VITA_THREAD_STACK_SIZE_MAX) { + thread->stacksize = VITA_THREAD_STACK_SIZE_MAX; + } + stack_size = thread->stacksize; + } + + // Create new thread with the same priority as the current thread + thread->handle = sceKernelCreateThread( + thread_name, // name + ThreadEntry, // function to run + 0, // priority. 0 means priority of calling thread + stack_size, // stack size + 0, // attributes. always 0 + 0, // cpu affinity mask. 0 = all CPUs + NULL // opt. always NULL + ); + + if (thread->handle < 0) { + return SDL_SetError("sceKernelCreateThread() failed"); + } + + sceKernelStartThread(thread->handle, 4, &thread); + return true; +} + +void SDL_SYS_SetupThread(const char *name) +{ + // Do nothing. +} + +SDL_ThreadID SDL_GetCurrentThreadID(void) +{ + return (SDL_ThreadID)sceKernelGetThreadId(); +} + +void SDL_SYS_WaitThread(SDL_Thread *thread) +{ + sceKernelWaitThreadEnd(thread->handle, NULL, NULL); + sceKernelDeleteThread(thread->handle); +} + +void SDL_SYS_DetachThread(SDL_Thread *thread) +{ + // Do nothing. +} + +bool SDL_SYS_SetThreadPriority(SDL_ThreadPriority priority) +{ + int value = VITA_THREAD_PRIORITY_NORMAL; + + switch (priority) { + case SDL_THREAD_PRIORITY_LOW: + value = VITA_THREAD_PRIORITY_LOW; + break; + case SDL_THREAD_PRIORITY_NORMAL: + value = VITA_THREAD_PRIORITY_NORMAL; + break; + case SDL_THREAD_PRIORITY_HIGH: + value = VITA_THREAD_PRIORITY_HIGH; + break; + case SDL_THREAD_PRIORITY_TIME_CRITICAL: + value = VITA_THREAD_PRIORITY_TIME_CRITICAL; + break; + } + + if (sceKernelChangeThreadPriority(0, value) < 0) { + return SDL_SetError("sceKernelChangeThreadPriority() failed"); + } + return true; +} + +#endif // SDL_THREAD_VITA diff --git a/contrib/SDL-3.2.8/src/thread/vita/SDL_systhread_c.h b/contrib/SDL-3.2.8/src/thread/vita/SDL_systhread_c.h new file mode 100644 index 0000000..b231ec7 --- /dev/null +++ b/contrib/SDL-3.2.8/src/thread/vita/SDL_systhread_c.h @@ -0,0 +1,24 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + 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 + +typedef SceUID SYS_ThreadHandle; diff --git a/contrib/SDL-3.2.8/src/thread/windows/SDL_syscond_cv.c b/contrib/SDL-3.2.8/src/thread/windows/SDL_syscond_cv.c new file mode 100644 index 0000000..b29ef63 --- /dev/null +++ b/contrib/SDL-3.2.8/src/thread/windows/SDL_syscond_cv.c @@ -0,0 +1,226 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + 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); +} diff --git a/contrib/SDL-3.2.8/src/thread/windows/SDL_sysmutex.c b/contrib/SDL-3.2.8/src/thread/windows/SDL_sysmutex.c new file mode 100644 index 0000000..88ec004 --- /dev/null +++ b/contrib/SDL-3.2.8/src/thread/windows/SDL_sysmutex.c @@ -0,0 +1,238 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + 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 diff --git a/contrib/SDL-3.2.8/src/thread/windows/SDL_sysmutex_c.h b/contrib/SDL-3.2.8/src/thread/windows/SDL_sysmutex_c.h new file mode 100644 index 0000000..762dc7c --- /dev/null +++ b/contrib/SDL-3.2.8/src/thread/windows/SDL_sysmutex_c.h @@ -0,0 +1,73 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + 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; diff --git a/contrib/SDL-3.2.8/src/thread/windows/SDL_sysrwlock_srw.c b/contrib/SDL-3.2.8/src/thread/windows/SDL_sysrwlock_srw.c new file mode 100644 index 0000000..ca1a48e --- /dev/null +++ b/contrib/SDL-3.2.8/src/thread/windows/SDL_sysrwlock_srw.c @@ -0,0 +1,231 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + 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); + } +} + diff --git a/contrib/SDL-3.2.8/src/thread/windows/SDL_syssem.c b/contrib/SDL-3.2.8/src/thread/windows/SDL_syssem.c new file mode 100644 index 0000000..ba35add --- /dev/null +++ b/contrib/SDL-3.2.8/src/thread/windows/SDL_syssem.c @@ -0,0 +1,351 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + 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 diff --git a/contrib/SDL-3.2.8/src/thread/windows/SDL_systhread.c b/contrib/SDL-3.2.8/src/thread/windows/SDL_systhread.c new file mode 100644 index 0000000..c5bee81 --- /dev/null +++ b/contrib/SDL-3.2.8/src/thread/windows/SDL_systhread.c @@ -0,0 +1,197 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + 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 diff --git a/contrib/SDL-3.2.8/src/thread/windows/SDL_systhread_c.h b/contrib/SDL-3.2.8/src/thread/windows/SDL_systhread_c.h new file mode 100644 index 0000000..5787abb --- /dev/null +++ b/contrib/SDL-3.2.8/src/thread/windows/SDL_systhread_c.h @@ -0,0 +1,30 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + 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_ diff --git a/contrib/SDL-3.2.8/src/thread/windows/SDL_systls.c b/contrib/SDL-3.2.8/src/thread/windows/SDL_systls.c new file mode 100644 index 0000000..354016a --- /dev/null +++ b/contrib/SDL-3.2.8/src/thread/windows/SDL_systls.c @@ -0,0 +1,81 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + 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 -- cgit v1.2.3