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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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