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 --- .../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 +++++ 8 files changed, 1427 insertions(+) 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/windows') 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