summaryrefslogtreecommitdiff
path: root/contrib/SDL-3.2.8/src/thread/windows
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/SDL-3.2.8/src/thread/windows')
-rw-r--r--contrib/SDL-3.2.8/src/thread/windows/SDL_syscond_cv.c226
-rw-r--r--contrib/SDL-3.2.8/src/thread/windows/SDL_sysmutex.c238
-rw-r--r--contrib/SDL-3.2.8/src/thread/windows/SDL_sysmutex_c.h73
-rw-r--r--contrib/SDL-3.2.8/src/thread/windows/SDL_sysrwlock_srw.c231
-rw-r--r--contrib/SDL-3.2.8/src/thread/windows/SDL_syssem.c351
-rw-r--r--contrib/SDL-3.2.8/src/thread/windows/SDL_systhread.c197
-rw-r--r--contrib/SDL-3.2.8/src/thread/windows/SDL_systhread_c.h30
-rw-r--r--contrib/SDL-3.2.8/src/thread/windows/SDL_systls.c81
8 files changed, 1427 insertions, 0 deletions
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 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "SDL_internal.h"
22
23#include "../generic/SDL_syscond_c.h"
24#include "SDL_sysmutex_c.h"
25
26typedef SDL_Condition *(*pfnSDL_CreateCondition)(void);
27typedef void (*pfnSDL_DestroyCondition)(SDL_Condition *);
28typedef void (*pfnSDL_SignalCondition)(SDL_Condition *);
29typedef void (*pfnSDL_BroadcastCondition)(SDL_Condition *);
30typedef bool (*pfnSDL_WaitConditionTimeoutNS)(SDL_Condition *, SDL_Mutex *, Sint64);
31
32typedef struct SDL_cond_impl_t
33{
34 pfnSDL_CreateCondition Create;
35 pfnSDL_DestroyCondition Destroy;
36 pfnSDL_SignalCondition Signal;
37 pfnSDL_BroadcastCondition Broadcast;
38 pfnSDL_WaitConditionTimeoutNS WaitTimeoutNS;
39} SDL_cond_impl_t;
40
41// Implementation will be chosen at runtime based on available Kernel features
42static SDL_cond_impl_t SDL_cond_impl_active = { 0 };
43
44/**
45 * Native Windows Condition Variable (SRW Locks)
46 */
47
48#ifndef CONDITION_VARIABLE_INIT
49#define CONDITION_VARIABLE_INIT \
50 { \
51 0 \
52 }
53typedef struct CONDITION_VARIABLE
54{
55 PVOID Ptr;
56} CONDITION_VARIABLE, *PCONDITION_VARIABLE;
57#endif
58
59typedef VOID(WINAPI *pfnWakeConditionVariable)(PCONDITION_VARIABLE);
60typedef VOID(WINAPI *pfnWakeAllConditionVariable)(PCONDITION_VARIABLE);
61typedef BOOL(WINAPI *pfnSleepConditionVariableSRW)(PCONDITION_VARIABLE, PSRWLOCK, DWORD, ULONG);
62typedef BOOL(WINAPI *pfnSleepConditionVariableCS)(PCONDITION_VARIABLE, PCRITICAL_SECTION, DWORD);
63
64static pfnWakeConditionVariable pWakeConditionVariable = NULL;
65static pfnWakeAllConditionVariable pWakeAllConditionVariable = NULL;
66static pfnSleepConditionVariableSRW pSleepConditionVariableSRW = NULL;
67static pfnSleepConditionVariableCS pSleepConditionVariableCS = NULL;
68
69typedef struct SDL_cond_cv
70{
71 CONDITION_VARIABLE cond;
72} SDL_cond_cv;
73
74static SDL_Condition *SDL_CreateCondition_cv(void)
75{
76 // Relies on CONDITION_VARIABLE_INIT == 0.
77 return (SDL_Condition *)SDL_calloc(1, sizeof(SDL_cond_cv));
78}
79
80static void SDL_DestroyCondition_cv(SDL_Condition *cond)
81{
82 // There are no kernel allocated resources
83 SDL_free(cond);
84}
85
86static void SDL_SignalCondition_cv(SDL_Condition *_cond)
87{
88 SDL_cond_cv *cond = (SDL_cond_cv *)_cond;
89 pWakeConditionVariable(&cond->cond);
90}
91
92static void SDL_BroadcastCondition_cv(SDL_Condition *_cond)
93{
94 SDL_cond_cv *cond = (SDL_cond_cv *)_cond;
95 pWakeAllConditionVariable(&cond->cond);
96}
97
98static bool SDL_WaitConditionTimeoutNS_cv(SDL_Condition *_cond, SDL_Mutex *_mutex, Sint64 timeoutNS)
99{
100 SDL_cond_cv *cond = (SDL_cond_cv *)_cond;
101 DWORD timeout;
102 bool result;
103
104 if (timeoutNS < 0) {
105 timeout = INFINITE;
106 } else {
107 timeout = (DWORD)SDL_NS_TO_MS(timeoutNS);
108 }
109
110 if (SDL_mutex_impl_active.Type == SDL_MUTEX_SRW) {
111 SDL_mutex_srw *mutex = (SDL_mutex_srw *)_mutex;
112
113 if (mutex->count != 1 || mutex->owner != GetCurrentThreadId()) {
114 // Passed mutex is not locked or locked recursively"
115 return false;
116 }
117
118 // The mutex must be updated to the released state
119 mutex->count = 0;
120 mutex->owner = 0;
121
122 result = (pSleepConditionVariableSRW(&cond->cond, &mutex->srw, timeout, 0) == TRUE);
123
124 // The mutex is owned by us again, regardless of status of the wait
125 SDL_assert(mutex->count == 0 && mutex->owner == 0);
126 mutex->count = 1;
127 mutex->owner = GetCurrentThreadId();
128 } else {
129 SDL_mutex_cs *mutex = (SDL_mutex_cs *)_mutex;
130
131 SDL_assert(SDL_mutex_impl_active.Type == SDL_MUTEX_CS);
132
133 result = (pSleepConditionVariableCS(&cond->cond, &mutex->cs, timeout) == TRUE);
134 }
135
136 return result;
137}
138
139static const SDL_cond_impl_t SDL_cond_impl_cv = {
140 &SDL_CreateCondition_cv,
141 &SDL_DestroyCondition_cv,
142 &SDL_SignalCondition_cv,
143 &SDL_BroadcastCondition_cv,
144 &SDL_WaitConditionTimeoutNS_cv,
145};
146
147
148// Generic Condition Variable implementation using SDL_Mutex and SDL_Semaphore
149static const SDL_cond_impl_t SDL_cond_impl_generic = {
150 &SDL_CreateCondition_generic,
151 &SDL_DestroyCondition_generic,
152 &SDL_SignalCondition_generic,
153 &SDL_BroadcastCondition_generic,
154 &SDL_WaitConditionTimeoutNS_generic,
155};
156
157SDL_Condition *SDL_CreateCondition(void)
158{
159 if (!SDL_cond_impl_active.Create) {
160 const SDL_cond_impl_t *impl = NULL;
161
162 if (SDL_mutex_impl_active.Type == SDL_MUTEX_INVALID) {
163 // The mutex implementation isn't decided yet, trigger it
164 SDL_Mutex *mutex = SDL_CreateMutex();
165 if (!mutex) {
166 return NULL;
167 }
168 SDL_DestroyMutex(mutex);
169
170 SDL_assert(SDL_mutex_impl_active.Type != SDL_MUTEX_INVALID);
171 }
172
173 // Default to generic implementation, works with all mutex implementations
174 impl = &SDL_cond_impl_generic;
175 {
176 HMODULE kernel32 = GetModuleHandle(TEXT("kernel32.dll"));
177 if (kernel32) {
178 pWakeConditionVariable = (pfnWakeConditionVariable)GetProcAddress(kernel32, "WakeConditionVariable");
179 pWakeAllConditionVariable = (pfnWakeAllConditionVariable)GetProcAddress(kernel32, "WakeAllConditionVariable");
180 pSleepConditionVariableSRW = (pfnSleepConditionVariableSRW)GetProcAddress(kernel32, "SleepConditionVariableSRW");
181 pSleepConditionVariableCS = (pfnSleepConditionVariableCS)GetProcAddress(kernel32, "SleepConditionVariableCS");
182 if (pWakeConditionVariable && pWakeAllConditionVariable && pSleepConditionVariableSRW && pSleepConditionVariableCS) {
183 // Use the Windows provided API
184 impl = &SDL_cond_impl_cv;
185 }
186 }
187 }
188
189 SDL_copyp(&SDL_cond_impl_active, impl);
190 }
191 return SDL_cond_impl_active.Create();
192}
193
194void SDL_DestroyCondition(SDL_Condition *cond)
195{
196 if (cond) {
197 SDL_cond_impl_active.Destroy(cond);
198 }
199}
200
201void SDL_SignalCondition(SDL_Condition *cond)
202{
203 if (!cond) {
204 return;
205 }
206
207 SDL_cond_impl_active.Signal(cond);
208}
209
210void SDL_BroadcastCondition(SDL_Condition *cond)
211{
212 if (!cond) {
213 return;
214 }
215
216 SDL_cond_impl_active.Broadcast(cond);
217}
218
219bool SDL_WaitConditionTimeoutNS(SDL_Condition *cond, SDL_Mutex *mutex, Sint64 timeoutNS)
220{
221 if (!cond || !mutex) {
222 return true;
223 }
224
225 return SDL_cond_impl_active.WaitTimeoutNS(cond, mutex, timeoutNS);
226}
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 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "SDL_internal.h"
22
23#ifdef SDL_THREAD_WINDOWS
24
25/**
26 * Mutex functions using the Win32 API
27 * There are two implementations available based on:
28 * - Critical Sections. Available on all OS versions since Windows XP.
29 * - Slim Reader/Writer Locks. Requires Windows 7 or newer.
30 * which are chosen at runtime.
31 */
32
33#include "SDL_sysmutex_c.h"
34
35// Implementation will be chosen at runtime based on available Kernel features
36SDL_mutex_impl_t SDL_mutex_impl_active = { 0 };
37
38/**
39 * Implementation based on Slim Reader/Writer (SRW) Locks for Win 7 and newer.
40 */
41
42typedef VOID(WINAPI *pfnInitializeSRWLock)(PSRWLOCK);
43typedef VOID(WINAPI *pfnReleaseSRWLockExclusive)(PSRWLOCK);
44typedef VOID(WINAPI *pfnAcquireSRWLockExclusive)(PSRWLOCK);
45typedef BOOLEAN(WINAPI *pfnTryAcquireSRWLockExclusive)(PSRWLOCK);
46static pfnInitializeSRWLock pInitializeSRWLock = NULL;
47static pfnReleaseSRWLockExclusive pReleaseSRWLockExclusive = NULL;
48static pfnAcquireSRWLockExclusive pAcquireSRWLockExclusive = NULL;
49static pfnTryAcquireSRWLockExclusive pTryAcquireSRWLockExclusive = NULL;
50
51static SDL_Mutex *SDL_CreateMutex_srw(void)
52{
53 SDL_mutex_srw *mutex = (SDL_mutex_srw *)SDL_calloc(1, sizeof(*mutex));
54 if (mutex) {
55 pInitializeSRWLock(&mutex->srw);
56 }
57 return (SDL_Mutex *)mutex;
58}
59
60static void SDL_DestroyMutex_srw(SDL_Mutex *mutex)
61{
62 // There are no kernel allocated resources
63 SDL_free(mutex);
64}
65
66static void SDL_LockMutex_srw(SDL_Mutex *_mutex) SDL_NO_THREAD_SAFETY_ANALYSIS // clang doesn't know about NULL mutexes
67{
68 SDL_mutex_srw *mutex = (SDL_mutex_srw *)_mutex;
69 const DWORD this_thread = GetCurrentThreadId();
70
71 if (mutex->owner == this_thread) {
72 ++mutex->count;
73 } else {
74 /* The order of operations is important.
75 We set the locking thread id after we obtain the lock
76 so unlocks from other threads will fail.
77 */
78 pAcquireSRWLockExclusive(&mutex->srw);
79 SDL_assert(mutex->count == 0 && mutex->owner == 0);
80 mutex->owner = this_thread;
81 mutex->count = 1;
82 }
83}
84
85static bool SDL_TryLockMutex_srw(SDL_Mutex *_mutex)
86{
87 SDL_mutex_srw *mutex = (SDL_mutex_srw *)_mutex;
88 const DWORD this_thread = GetCurrentThreadId();
89 bool retval = true;
90
91 if (mutex->owner == this_thread) {
92 ++mutex->count;
93 } else {
94 if (pTryAcquireSRWLockExclusive(&mutex->srw) != 0) {
95 SDL_assert(mutex->count == 0 && mutex->owner == 0);
96 mutex->owner = this_thread;
97 mutex->count = 1;
98 } else {
99 retval = false;
100 }
101 }
102 return retval;
103}
104
105static void SDL_UnlockMutex_srw(SDL_Mutex *_mutex) SDL_NO_THREAD_SAFETY_ANALYSIS // clang doesn't know about NULL mutexes
106{
107 SDL_mutex_srw *mutex = (SDL_mutex_srw *)_mutex;
108
109 if (mutex->owner == GetCurrentThreadId()) {
110 if (--mutex->count == 0) {
111 mutex->owner = 0;
112 pReleaseSRWLockExclusive(&mutex->srw);
113 }
114 } else {
115 SDL_assert(!"mutex not owned by this thread"); // undefined behavior...!
116 }
117}
118
119static const SDL_mutex_impl_t SDL_mutex_impl_srw = {
120 &SDL_CreateMutex_srw,
121 &SDL_DestroyMutex_srw,
122 &SDL_LockMutex_srw,
123 &SDL_TryLockMutex_srw,
124 &SDL_UnlockMutex_srw,
125 SDL_MUTEX_SRW,
126};
127
128/**
129 * Fallback Mutex implementation using Critical Sections (before Win 7)
130 */
131
132static SDL_Mutex *SDL_CreateMutex_cs(void)
133{
134 SDL_mutex_cs *mutex = (SDL_mutex_cs *)SDL_malloc(sizeof(*mutex));
135 if (mutex) {
136 // Initialize
137 // On SMP systems, a non-zero spin count generally helps performance
138 // This function always succeeds
139 (void)InitializeCriticalSectionAndSpinCount(&mutex->cs, 2000);
140 }
141 return (SDL_Mutex *)mutex;
142}
143
144static void SDL_DestroyMutex_cs(SDL_Mutex *mutex_)
145{
146 SDL_mutex_cs *mutex = (SDL_mutex_cs *)mutex_;
147 DeleteCriticalSection(&mutex->cs);
148 SDL_free(mutex);
149}
150
151static void SDL_LockMutex_cs(SDL_Mutex *mutex_) SDL_NO_THREAD_SAFETY_ANALYSIS // clang doesn't know about NULL mutexes
152{
153 SDL_mutex_cs *mutex = (SDL_mutex_cs *)mutex_;
154 EnterCriticalSection(&mutex->cs);
155}
156
157static bool SDL_TryLockMutex_cs(SDL_Mutex *mutex_)
158{
159 SDL_mutex_cs *mutex = (SDL_mutex_cs *)mutex_;
160 return (TryEnterCriticalSection(&mutex->cs) == TRUE);
161}
162
163static void SDL_UnlockMutex_cs(SDL_Mutex *mutex_) SDL_NO_THREAD_SAFETY_ANALYSIS // clang doesn't know about NULL mutexes
164{
165 SDL_mutex_cs *mutex = (SDL_mutex_cs *)mutex_;
166 LeaveCriticalSection(&mutex->cs);
167}
168
169static const SDL_mutex_impl_t SDL_mutex_impl_cs = {
170 &SDL_CreateMutex_cs,
171 &SDL_DestroyMutex_cs,
172 &SDL_LockMutex_cs,
173 &SDL_TryLockMutex_cs,
174 &SDL_UnlockMutex_cs,
175 SDL_MUTEX_CS,
176};
177
178/**
179 * Runtime selection and redirection
180 */
181
182SDL_Mutex *SDL_CreateMutex(void)
183{
184 if (!SDL_mutex_impl_active.Create) {
185 const SDL_mutex_impl_t *impl = &SDL_mutex_impl_cs;
186
187 // Try faster implementation for Windows 7 and newer
188 HMODULE kernel32 = GetModuleHandle(TEXT("kernel32.dll"));
189 if (kernel32) {
190 // Requires Vista:
191 pInitializeSRWLock = (pfnInitializeSRWLock)GetProcAddress(kernel32, "InitializeSRWLock");
192 pReleaseSRWLockExclusive = (pfnReleaseSRWLockExclusive)GetProcAddress(kernel32, "ReleaseSRWLockExclusive");
193 pAcquireSRWLockExclusive = (pfnAcquireSRWLockExclusive)GetProcAddress(kernel32, "AcquireSRWLockExclusive");
194 // Requires 7:
195 pTryAcquireSRWLockExclusive = (pfnTryAcquireSRWLockExclusive)GetProcAddress(kernel32, "TryAcquireSRWLockExclusive");
196 if (pInitializeSRWLock && pReleaseSRWLockExclusive && pAcquireSRWLockExclusive && pTryAcquireSRWLockExclusive) {
197 impl = &SDL_mutex_impl_srw;
198 }
199 }
200
201 // Copy instead of using pointer to save one level of indirection
202 SDL_copyp(&SDL_mutex_impl_active, impl);
203 }
204 return SDL_mutex_impl_active.Create();
205}
206
207void SDL_DestroyMutex(SDL_Mutex *mutex)
208{
209 if (mutex) {
210 SDL_mutex_impl_active.Destroy(mutex);
211 }
212}
213
214void SDL_LockMutex(SDL_Mutex *mutex)
215{
216 if (mutex) {
217 SDL_mutex_impl_active.Lock(mutex);
218 }
219}
220
221bool SDL_TryLockMutex(SDL_Mutex *mutex)
222{
223 bool result = true;
224
225 if (mutex) {
226 result = SDL_mutex_impl_active.TryLock(mutex);
227 }
228 return result;
229}
230
231void SDL_UnlockMutex(SDL_Mutex *mutex)
232{
233 if (mutex) {
234 SDL_mutex_impl_active.Unlock(mutex);
235 }
236}
237
238#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 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "SDL_internal.h"
22
23#include "../../core/windows/SDL_windows.h"
24
25typedef SDL_Mutex *(*pfnSDL_CreateMutex)(void);
26typedef void (*pfnSDL_LockMutex)(SDL_Mutex *);
27typedef bool (*pfnSDL_TryLockMutex)(SDL_Mutex *);
28typedef void (*pfnSDL_UnlockMutex)(SDL_Mutex *);
29typedef void (*pfnSDL_DestroyMutex)(SDL_Mutex *);
30
31typedef enum
32{
33 SDL_MUTEX_INVALID = 0,
34 SDL_MUTEX_SRW,
35 SDL_MUTEX_CS,
36} SDL_MutexType;
37
38typedef struct SDL_mutex_impl_t
39{
40 pfnSDL_CreateMutex Create;
41 pfnSDL_DestroyMutex Destroy;
42 pfnSDL_LockMutex Lock;
43 pfnSDL_TryLockMutex TryLock;
44 pfnSDL_UnlockMutex Unlock;
45 // Needed by SDL_Condition:
46 SDL_MutexType Type;
47} SDL_mutex_impl_t;
48
49extern SDL_mutex_impl_t SDL_mutex_impl_active;
50
51#ifndef SRWLOCK_INIT
52#define SRWLOCK_INIT \
53 { \
54 0 \
55 }
56typedef struct _SRWLOCK
57{
58 PVOID Ptr;
59} SRWLOCK, *PSRWLOCK;
60#endif
61
62typedef struct SDL_mutex_srw
63{
64 SRWLOCK srw;
65 // SRW Locks are not recursive, that has to be handled by SDL:
66 DWORD count;
67 DWORD owner;
68} SDL_mutex_srw;
69
70typedef struct SDL_mutex_cs
71{
72 CRITICAL_SECTION cs;
73} 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 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "SDL_internal.h"
22
23/**
24 * Implementation based on Slim Reader/Writer (SRW) Locks for Win 7 and newer.
25 */
26
27// This header makes sure SRWLOCK is actually declared, even on ancient WinSDKs.
28#include "SDL_sysmutex_c.h"
29
30typedef VOID(WINAPI *pfnInitializeSRWLock)(PSRWLOCK);
31typedef VOID(WINAPI *pfnReleaseSRWLockShared)(PSRWLOCK);
32typedef VOID(WINAPI *pfnAcquireSRWLockShared)(PSRWLOCK);
33typedef BOOLEAN(WINAPI *pfnTryAcquireSRWLockShared)(PSRWLOCK);
34typedef VOID(WINAPI *pfnReleaseSRWLockExclusive)(PSRWLOCK);
35typedef VOID(WINAPI *pfnAcquireSRWLockExclusive)(PSRWLOCK);
36typedef BOOLEAN(WINAPI *pfnTryAcquireSRWLockExclusive)(PSRWLOCK);
37
38static pfnInitializeSRWLock pInitializeSRWLock = NULL;
39static pfnReleaseSRWLockShared pReleaseSRWLockShared = NULL;
40static pfnAcquireSRWLockShared pAcquireSRWLockShared = NULL;
41static pfnTryAcquireSRWLockShared pTryAcquireSRWLockShared = NULL;
42static pfnReleaseSRWLockExclusive pReleaseSRWLockExclusive = NULL;
43static pfnAcquireSRWLockExclusive pAcquireSRWLockExclusive = NULL;
44static pfnTryAcquireSRWLockExclusive pTryAcquireSRWLockExclusive = NULL;
45
46typedef SDL_RWLock *(*pfnSDL_CreateRWLock)(void);
47typedef void (*pfnSDL_DestroyRWLock)(SDL_RWLock *);
48typedef void (*pfnSDL_LockRWLockForReading)(SDL_RWLock *);
49typedef void (*pfnSDL_LockRWLockForWriting)(SDL_RWLock *);
50typedef bool (*pfnSDL_TryLockRWLockForReading)(SDL_RWLock *);
51typedef bool (*pfnSDL_TryLockRWLockForWriting)(SDL_RWLock *);
52typedef void (*pfnSDL_UnlockRWLock)(SDL_RWLock *);
53
54typedef struct SDL_rwlock_impl_t
55{
56 pfnSDL_CreateRWLock Create;
57 pfnSDL_DestroyRWLock Destroy;
58 pfnSDL_LockRWLockForReading LockForReading;
59 pfnSDL_LockRWLockForWriting LockForWriting;
60 pfnSDL_TryLockRWLockForReading TryLockForReading;
61 pfnSDL_TryLockRWLockForWriting TryLockForWriting;
62 pfnSDL_UnlockRWLock Unlock;
63} SDL_rwlock_impl_t;
64
65// Implementation will be chosen at runtime based on available Kernel features
66static SDL_rwlock_impl_t SDL_rwlock_impl_active = { 0 };
67
68// rwlock implementation using Win7+ slim read/write locks (SRWLOCK)
69
70typedef struct SDL_rwlock_srw
71{
72 SRWLOCK srw;
73 SDL_ThreadID write_owner;
74} SDL_rwlock_srw;
75
76static SDL_RWLock *SDL_CreateRWLock_srw(void)
77{
78 SDL_rwlock_srw *rwlock = (SDL_rwlock_srw *)SDL_calloc(1, sizeof(*rwlock));
79 if (rwlock) {
80 pInitializeSRWLock(&rwlock->srw);
81 }
82 return (SDL_RWLock *)rwlock;
83}
84
85static void SDL_DestroyRWLock_srw(SDL_RWLock *_rwlock)
86{
87 SDL_rwlock_srw *rwlock = (SDL_rwlock_srw *) _rwlock;
88 // There are no kernel allocated resources
89 SDL_free(rwlock);
90}
91
92static void SDL_LockRWLockForReading_srw(SDL_RWLock *_rwlock) SDL_NO_THREAD_SAFETY_ANALYSIS // clang doesn't know about NULL mutexes
93{
94 SDL_rwlock_srw *rwlock = (SDL_rwlock_srw *) _rwlock;
95 pAcquireSRWLockShared(&rwlock->srw);
96}
97
98static void SDL_LockRWLockForWriting_srw(SDL_RWLock *_rwlock) SDL_NO_THREAD_SAFETY_ANALYSIS // clang doesn't know about NULL mutexes
99{
100 SDL_rwlock_srw *rwlock = (SDL_rwlock_srw *) _rwlock;
101 pAcquireSRWLockExclusive(&rwlock->srw);
102 rwlock->write_owner = SDL_GetCurrentThreadID();
103}
104
105static bool SDL_TryLockRWLockForReading_srw(SDL_RWLock *_rwlock)
106{
107 SDL_rwlock_srw *rwlock = (SDL_rwlock_srw *) _rwlock;
108 return pTryAcquireSRWLockShared(&rwlock->srw);
109}
110
111static bool SDL_TryLockRWLockForWriting_srw(SDL_RWLock *_rwlock)
112{
113 SDL_rwlock_srw *rwlock = (SDL_rwlock_srw *) _rwlock;
114 if (pTryAcquireSRWLockExclusive(&rwlock->srw)) {
115 rwlock->write_owner = SDL_GetCurrentThreadID();
116 return true;
117 } else {
118 return false;
119 }
120}
121
122static void SDL_UnlockRWLock_srw(SDL_RWLock *_rwlock) SDL_NO_THREAD_SAFETY_ANALYSIS // clang doesn't know about NULL mutexes
123{
124 SDL_rwlock_srw *rwlock = (SDL_rwlock_srw *) _rwlock;
125 if (rwlock->write_owner == SDL_GetCurrentThreadID()) {
126 rwlock->write_owner = 0;
127 pReleaseSRWLockExclusive(&rwlock->srw);
128 } else {
129 pReleaseSRWLockShared(&rwlock->srw);
130 }
131}
132
133static const SDL_rwlock_impl_t SDL_rwlock_impl_srw = {
134 &SDL_CreateRWLock_srw,
135 &SDL_DestroyRWLock_srw,
136 &SDL_LockRWLockForReading_srw,
137 &SDL_LockRWLockForWriting_srw,
138 &SDL_TryLockRWLockForReading_srw,
139 &SDL_TryLockRWLockForWriting_srw,
140 &SDL_UnlockRWLock_srw
141};
142
143
144#include "../generic/SDL_sysrwlock_c.h"
145
146// Generic rwlock implementation using SDL_Mutex, SDL_Condition, and SDL_AtomicInt
147static const SDL_rwlock_impl_t SDL_rwlock_impl_generic = {
148 &SDL_CreateRWLock_generic,
149 &SDL_DestroyRWLock_generic,
150 &SDL_LockRWLockForReading_generic,
151 &SDL_LockRWLockForWriting_generic,
152 &SDL_TryLockRWLockForReading_generic,
153 &SDL_TryLockRWLockForWriting_generic,
154 &SDL_UnlockRWLock_generic
155};
156
157SDL_RWLock *SDL_CreateRWLock(void)
158{
159 if (!SDL_rwlock_impl_active.Create) {
160 // Default to generic implementation, works with all mutex implementations
161 const SDL_rwlock_impl_t *impl = &SDL_rwlock_impl_generic;
162 {
163 HMODULE kernel32 = GetModuleHandle(TEXT("kernel32.dll"));
164 if (kernel32) {
165 bool okay = true;
166 #define LOOKUP_SRW_SYM(sym) if (okay) { if ((p##sym = (pfn##sym)GetProcAddress(kernel32, #sym)) == NULL) { okay = false; } }
167 LOOKUP_SRW_SYM(InitializeSRWLock);
168 LOOKUP_SRW_SYM(ReleaseSRWLockShared);
169 LOOKUP_SRW_SYM(AcquireSRWLockShared);
170 LOOKUP_SRW_SYM(TryAcquireSRWLockShared);
171 LOOKUP_SRW_SYM(ReleaseSRWLockExclusive);
172 LOOKUP_SRW_SYM(AcquireSRWLockExclusive);
173 LOOKUP_SRW_SYM(TryAcquireSRWLockExclusive);
174 #undef LOOKUP_SRW_SYM
175 if (okay) {
176 impl = &SDL_rwlock_impl_srw; // Use the Windows provided API instead of generic fallback
177 }
178 }
179 }
180
181 SDL_copyp(&SDL_rwlock_impl_active, impl);
182 }
183 return SDL_rwlock_impl_active.Create();
184}
185
186void SDL_DestroyRWLock(SDL_RWLock *rwlock)
187{
188 if (rwlock) {
189 SDL_rwlock_impl_active.Destroy(rwlock);
190 }
191}
192
193void SDL_LockRWLockForReading(SDL_RWLock *rwlock) SDL_NO_THREAD_SAFETY_ANALYSIS // clang doesn't know about NULL mutexes
194{
195 if (rwlock) {
196 SDL_rwlock_impl_active.LockForReading(rwlock);
197 }
198}
199
200void SDL_LockRWLockForWriting(SDL_RWLock *rwlock) SDL_NO_THREAD_SAFETY_ANALYSIS // clang doesn't know about NULL mutexes
201{
202 if (rwlock) {
203 SDL_rwlock_impl_active.LockForWriting(rwlock);
204 }
205}
206
207bool SDL_TryLockRWLockForReading(SDL_RWLock *rwlock)
208{
209 bool result = true;
210 if (rwlock) {
211 result = SDL_rwlock_impl_active.TryLockForReading(rwlock);
212 }
213 return result;
214}
215
216bool SDL_TryLockRWLockForWriting(SDL_RWLock *rwlock)
217{
218 bool result = true;
219 if (rwlock) {
220 result = SDL_rwlock_impl_active.TryLockForWriting(rwlock);
221 }
222 return result;
223}
224
225void SDL_UnlockRWLock(SDL_RWLock *rwlock) SDL_NO_THREAD_SAFETY_ANALYSIS // clang doesn't know about NULL mutexes
226{
227 if (rwlock) {
228 SDL_rwlock_impl_active.Unlock(rwlock);
229 }
230}
231
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 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "SDL_internal.h"
22
23#ifdef SDL_THREAD_WINDOWS
24
25/**
26 * Semaphore functions using the Win32 API
27 * There are two implementations available based on:
28 * - Kernel Semaphores. Available on all OS versions. (kern)
29 * Heavy-weight inter-process kernel objects.
30 * - Atomics and WaitOnAddress API. (atom)
31 * Faster due to significantly less context switches.
32 * Requires Windows 8 or newer.
33 * which are chosen at runtime.
34 */
35
36#include "../../core/windows/SDL_windows.h"
37
38typedef SDL_Semaphore *(*pfnSDL_CreateSemaphore)(Uint32);
39typedef void (*pfnSDL_DestroySemaphore)(SDL_Semaphore *);
40typedef bool (*pfnSDL_WaitSemaphoreTimeoutNS)(SDL_Semaphore *, Sint64);
41typedef Uint32 (*pfnSDL_GetSemaphoreValue)(SDL_Semaphore *);
42typedef void (*pfnSDL_SignalSemaphore)(SDL_Semaphore *);
43
44typedef struct SDL_semaphore_impl_t
45{
46 pfnSDL_CreateSemaphore Create;
47 pfnSDL_DestroySemaphore Destroy;
48 pfnSDL_WaitSemaphoreTimeoutNS WaitTimeoutNS;
49 pfnSDL_GetSemaphoreValue Value;
50 pfnSDL_SignalSemaphore Signal;
51} SDL_sem_impl_t;
52
53// Implementation will be chosen at runtime based on available Kernel features
54static SDL_sem_impl_t SDL_sem_impl_active = { 0 };
55
56/**
57 * Atomic + WaitOnAddress implementation
58 */
59
60// APIs not available on WinPhone 8.1
61// https://www.microsoft.com/en-us/download/details.aspx?id=47328
62
63typedef BOOL(WINAPI *pfnWaitOnAddress)(volatile VOID *, PVOID, SIZE_T, DWORD);
64typedef VOID(WINAPI *pfnWakeByAddressSingle)(PVOID);
65
66static pfnWaitOnAddress pWaitOnAddress = NULL;
67static pfnWakeByAddressSingle pWakeByAddressSingle = NULL;
68
69typedef struct SDL_semaphore_atom
70{
71 LONG count;
72} SDL_sem_atom;
73
74static SDL_Semaphore *SDL_CreateSemaphore_atom(Uint32 initial_value)
75{
76 SDL_sem_atom *sem;
77
78 sem = (SDL_sem_atom *)SDL_malloc(sizeof(*sem));
79 if (sem) {
80 sem->count = initial_value;
81 }
82 return (SDL_Semaphore *)sem;
83}
84
85static void SDL_DestroySemaphore_atom(SDL_Semaphore *sem)
86{
87 SDL_free(sem);
88}
89
90static bool SDL_WaitSemaphoreTimeoutNS_atom(SDL_Semaphore *_sem, Sint64 timeoutNS)
91{
92 SDL_sem_atom *sem = (SDL_sem_atom *)_sem;
93 LONG count;
94 Uint64 now;
95 Uint64 deadline;
96 DWORD timeout_eff;
97
98 if (!sem) {
99 return true;
100 }
101
102 if (timeoutNS == 0) {
103 count = sem->count;
104 if (count == 0) {
105 return false;
106 }
107
108 if (InterlockedCompareExchange(&sem->count, count - 1, count) == count) {
109 return true;
110 }
111
112 return false;
113 }
114
115 if (timeoutNS < 0) {
116 for (;;) {
117 count = sem->count;
118 while (count == 0) {
119 if (!pWaitOnAddress(&sem->count, &count, sizeof(sem->count), INFINITE)) {
120 return false;
121 }
122 count = sem->count;
123 }
124
125 if (InterlockedCompareExchange(&sem->count, count - 1, count) == count) {
126 return true;
127 }
128 }
129 }
130
131 /**
132 * WaitOnAddress is subject to spurious and stolen wakeups so we
133 * need to recalculate the effective timeout before every wait
134 */
135 now = SDL_GetTicksNS();
136 deadline = now + timeoutNS;
137
138 for (;;) {
139 count = sem->count;
140 // If no semaphore is available we need to wait
141 while (count == 0) {
142 now = SDL_GetTicksNS();
143 if (deadline > now) {
144 timeout_eff = (DWORD)SDL_NS_TO_MS(deadline - now);
145 } else {
146 return false;
147 }
148 if (!pWaitOnAddress(&sem->count, &count, sizeof(count), timeout_eff)) {
149 return false;
150 }
151 count = sem->count;
152 }
153
154 // Actually the semaphore is only consumed if this succeeds
155 // If it doesn't we need to do everything again
156 if (InterlockedCompareExchange(&sem->count, count - 1, count) == count) {
157 return true;
158 }
159 }
160}
161
162static Uint32 SDL_GetSemaphoreValue_atom(SDL_Semaphore *_sem)
163{
164 SDL_sem_atom *sem = (SDL_sem_atom *)_sem;
165
166 if (!sem) {
167 return 0;
168 }
169
170 return (Uint32)sem->count;
171}
172
173static void SDL_SignalSemaphore_atom(SDL_Semaphore *_sem)
174{
175 SDL_sem_atom *sem = (SDL_sem_atom *)_sem;
176
177 if (!sem) {
178 return;
179 }
180
181 InterlockedIncrement(&sem->count);
182 pWakeByAddressSingle(&sem->count);
183}
184
185static const SDL_sem_impl_t SDL_sem_impl_atom = {
186 &SDL_CreateSemaphore_atom,
187 &SDL_DestroySemaphore_atom,
188 &SDL_WaitSemaphoreTimeoutNS_atom,
189 &SDL_GetSemaphoreValue_atom,
190 &SDL_SignalSemaphore_atom,
191};
192
193/**
194 * Fallback Semaphore implementation using Kernel Semaphores
195 */
196
197typedef struct SDL_semaphore_kern
198{
199 HANDLE id;
200 LONG count;
201} SDL_sem_kern;
202
203// Create a semaphore
204static SDL_Semaphore *SDL_CreateSemaphore_kern(Uint32 initial_value)
205{
206 SDL_sem_kern *sem;
207
208 // Allocate sem memory
209 sem = (SDL_sem_kern *)SDL_malloc(sizeof(*sem));
210 if (sem) {
211 // Create the semaphore, with max value 32K
212 sem->id = CreateSemaphore(NULL, initial_value, 32 * 1024, NULL);
213 sem->count = initial_value;
214 if (!sem->id) {
215 SDL_SetError("Couldn't create semaphore");
216 SDL_free(sem);
217 sem = NULL;
218 }
219 }
220 return (SDL_Semaphore *)sem;
221}
222
223// Free the semaphore
224static void SDL_DestroySemaphore_kern(SDL_Semaphore *_sem)
225{
226 SDL_sem_kern *sem = (SDL_sem_kern *)_sem;
227 if (sem) {
228 if (sem->id) {
229 CloseHandle(sem->id);
230 sem->id = 0;
231 }
232 SDL_free(sem);
233 }
234}
235
236static bool SDL_WaitSemaphoreTimeoutNS_kern(SDL_Semaphore *_sem, Sint64 timeoutNS)
237{
238 SDL_sem_kern *sem = (SDL_sem_kern *)_sem;
239 DWORD dwMilliseconds;
240
241 if (!sem) {
242 return true;
243 }
244
245 if (timeoutNS < 0) {
246 dwMilliseconds = INFINITE;
247 } else {
248 dwMilliseconds = (DWORD)SDL_NS_TO_MS(timeoutNS);
249 }
250 switch (WaitForSingleObjectEx(sem->id, dwMilliseconds, FALSE)) {
251 case WAIT_OBJECT_0:
252 InterlockedDecrement(&sem->count);
253 return true;
254 default:
255 return false;
256 }
257}
258
259// Returns the current count of the semaphore
260static Uint32 SDL_GetSemaphoreValue_kern(SDL_Semaphore *_sem)
261{
262 SDL_sem_kern *sem = (SDL_sem_kern *)_sem;
263 if (!sem) {
264 return 0;
265 }
266 return (Uint32)sem->count;
267}
268
269static void SDL_SignalSemaphore_kern(SDL_Semaphore *_sem)
270{
271 SDL_sem_kern *sem = (SDL_sem_kern *)_sem;
272
273 if (!sem) {
274 return;
275 }
276
277 /* Increase the counter in the first place, because
278 * after a successful release the semaphore may
279 * immediately get destroyed by another thread which
280 * is waiting for this semaphore.
281 */
282 InterlockedIncrement(&sem->count);
283 if (ReleaseSemaphore(sem->id, 1, NULL) == FALSE) {
284 InterlockedDecrement(&sem->count); // restore
285 }
286}
287
288static const SDL_sem_impl_t SDL_sem_impl_kern = {
289 &SDL_CreateSemaphore_kern,
290 &SDL_DestroySemaphore_kern,
291 &SDL_WaitSemaphoreTimeoutNS_kern,
292 &SDL_GetSemaphoreValue_kern,
293 &SDL_SignalSemaphore_kern,
294};
295
296/**
297 * Runtime selection and redirection
298 */
299
300SDL_Semaphore *SDL_CreateSemaphore(Uint32 initial_value)
301{
302 if (!SDL_sem_impl_active.Create) {
303 // Default to fallback implementation
304 const SDL_sem_impl_t *impl = &SDL_sem_impl_kern;
305
306 if (!SDL_GetHintBoolean(SDL_HINT_WINDOWS_FORCE_SEMAPHORE_KERNEL, false)) {
307 /* We already statically link to features from this Api
308 * Set (e.g. WaitForSingleObject). Dynamically loading
309 * API Sets is not explicitly documented but according to
310 * Microsoft our specific use case is legal and correct:
311 * https://github.com/microsoft/STL/pull/593#issuecomment-655799859
312 */
313 HMODULE synch120 = GetModuleHandle(TEXT("api-ms-win-core-synch-l1-2-0.dll"));
314 if (synch120) {
315 // Try to load required functions provided by Win 8 or newer
316 pWaitOnAddress = (pfnWaitOnAddress)GetProcAddress(synch120, "WaitOnAddress");
317 pWakeByAddressSingle = (pfnWakeByAddressSingle)GetProcAddress(synch120, "WakeByAddressSingle");
318
319 if (pWaitOnAddress && pWakeByAddressSingle) {
320 impl = &SDL_sem_impl_atom;
321 }
322 }
323 }
324
325 // Copy instead of using pointer to save one level of indirection
326 SDL_copyp(&SDL_sem_impl_active, impl);
327 }
328 return SDL_sem_impl_active.Create(initial_value);
329}
330
331void SDL_DestroySemaphore(SDL_Semaphore *sem)
332{
333 SDL_sem_impl_active.Destroy(sem);
334}
335
336bool SDL_WaitSemaphoreTimeoutNS(SDL_Semaphore *sem, Sint64 timeoutNS)
337{
338 return SDL_sem_impl_active.WaitTimeoutNS(sem, timeoutNS);
339}
340
341Uint32 SDL_GetSemaphoreValue(SDL_Semaphore *sem)
342{
343 return SDL_sem_impl_active.Value(sem);
344}
345
346void SDL_SignalSemaphore(SDL_Semaphore *sem)
347{
348 SDL_sem_impl_active.Signal(sem);
349}
350
351#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 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "SDL_internal.h"
22
23#ifdef SDL_THREAD_WINDOWS
24
25// Win32 thread management routines for SDL
26
27#include "../SDL_thread_c.h"
28#include "../SDL_systhread.h"
29#include "SDL_systhread_c.h"
30
31#ifndef STACK_SIZE_PARAM_IS_A_RESERVATION
32#define STACK_SIZE_PARAM_IS_A_RESERVATION 0x00010000
33#endif
34
35#define SDL_DEBUGGER_NAME_EXCEPTION_CODE 0x406D1388
36
37typedef void (__cdecl * SDL_EndThreadExCallback) (unsigned retval);
38typedef uintptr_t (__cdecl * SDL_BeginThreadExCallback)
39 (void *security, unsigned stacksize, unsigned (__stdcall *startaddr)(void *),
40 void * arglist, unsigned initflag, unsigned *threadaddr);
41
42static DWORD RunThread(void *data)
43{
44 SDL_Thread *thread = (SDL_Thread *)data;
45 SDL_EndThreadExCallback pfnEndThread = (SDL_EndThreadExCallback)thread->endfunc;
46 SDL_RunThread(thread);
47 if (pfnEndThread) {
48 pfnEndThread(0);
49 }
50 return 0;
51}
52
53static DWORD WINAPI MINGW32_FORCEALIGN RunThreadViaCreateThread(LPVOID data)
54{
55 return RunThread(data);
56}
57
58static unsigned __stdcall MINGW32_FORCEALIGN RunThreadViaBeginThreadEx(void *data)
59{
60 return (unsigned)RunThread(data);
61}
62
63bool SDL_SYS_CreateThread(SDL_Thread *thread,
64 SDL_FunctionPointer vpfnBeginThread,
65 SDL_FunctionPointer vpfnEndThread)
66{
67 SDL_BeginThreadExCallback pfnBeginThread = (SDL_BeginThreadExCallback) vpfnBeginThread;
68
69 const DWORD flags = thread->stacksize ? STACK_SIZE_PARAM_IS_A_RESERVATION : 0;
70
71 // Save the function which we will have to call to clear the RTL of calling app!
72 thread->endfunc = vpfnEndThread;
73
74 // thread->stacksize == 0 means "system default", same as win32 expects
75 if (pfnBeginThread) {
76 unsigned threadid = 0;
77 thread->handle = (SYS_ThreadHandle)((size_t)pfnBeginThread(NULL, (unsigned int)thread->stacksize,
78 RunThreadViaBeginThreadEx,
79 thread, flags, &threadid));
80 } else {
81 DWORD threadid = 0;
82 thread->handle = CreateThread(NULL, thread->stacksize,
83 RunThreadViaCreateThread,
84 thread, flags, &threadid);
85 }
86 if (!thread->handle) {
87 return SDL_SetError("Not enough resources to create thread");
88 }
89 return true;
90}
91
92#pragma pack(push, 8)
93typedef struct tagTHREADNAME_INFO
94{
95 DWORD dwType; // must be 0x1000
96 LPCSTR szName; // pointer to name (in user addr space)
97 DWORD dwThreadID; // thread ID (-1=caller thread)
98 DWORD dwFlags; // reserved for future use, must be zero
99} THREADNAME_INFO;
100#pragma pack(pop)
101
102static LONG NTAPI EmptyVectoredExceptionHandler(EXCEPTION_POINTERS *info)
103{
104 if (info != NULL && info->ExceptionRecord != NULL && info->ExceptionRecord->ExceptionCode == SDL_DEBUGGER_NAME_EXCEPTION_CODE) {
105 return EXCEPTION_CONTINUE_EXECUTION;
106 } else {
107 return EXCEPTION_CONTINUE_SEARCH;
108 }
109}
110
111typedef HRESULT(WINAPI *pfnSetThreadDescription)(HANDLE, PCWSTR);
112
113void SDL_SYS_SetupThread(const char *name)
114{
115 if (name) {
116 PVOID exceptionHandlerHandle;
117 static pfnSetThreadDescription pSetThreadDescription = NULL;
118 static HMODULE kernel32 = NULL;
119
120 if (!kernel32) {
121 kernel32 = GetModuleHandle(TEXT("kernel32.dll"));
122 if (kernel32) {
123 pSetThreadDescription = (pfnSetThreadDescription)GetProcAddress(kernel32, "SetThreadDescription");
124 }
125 if (!kernel32 || !pSetThreadDescription) {
126 HMODULE kernelBase = GetModuleHandle(TEXT("KernelBase.dll"));
127 if (kernelBase) {
128 pSetThreadDescription = (pfnSetThreadDescription)GetProcAddress(kernelBase, "SetThreadDescription");
129 }
130 }
131 }
132
133 if (pSetThreadDescription) {
134 WCHAR *strw = WIN_UTF8ToStringW(name);
135 if (strw) {
136 pSetThreadDescription(GetCurrentThread(), strw);
137 SDL_free(strw);
138 }
139 }
140
141 /* Presumably some version of Visual Studio will understand SetThreadDescription(),
142 but we still need to deal with older OSes and debuggers. Set it with the arcane
143 exception magic, too. */
144
145 exceptionHandlerHandle = AddVectoredExceptionHandler(1, EmptyVectoredExceptionHandler);
146 if (exceptionHandlerHandle) {
147 THREADNAME_INFO inf;
148 // This magic tells the debugger to name a thread if it's listening.
149 SDL_zero(inf);
150 inf.dwType = 0x1000;
151 inf.szName = name;
152 inf.dwThreadID = (DWORD)-1;
153 inf.dwFlags = 0;
154
155 // The debugger catches this, renames the thread, continues on.
156 RaiseException(SDL_DEBUGGER_NAME_EXCEPTION_CODE, 0, sizeof(inf) / sizeof(ULONG_PTR), (const ULONG_PTR *)&inf);
157 RemoveVectoredExceptionHandler(exceptionHandlerHandle);
158 }
159 }
160}
161
162SDL_ThreadID SDL_GetCurrentThreadID(void)
163{
164 return (SDL_ThreadID)GetCurrentThreadId();
165}
166
167bool SDL_SYS_SetThreadPriority(SDL_ThreadPriority priority)
168{
169 int value;
170
171 if (priority == SDL_THREAD_PRIORITY_LOW) {
172 value = THREAD_PRIORITY_LOWEST;
173 } else if (priority == SDL_THREAD_PRIORITY_HIGH) {
174 value = THREAD_PRIORITY_HIGHEST;
175 } else if (priority == SDL_THREAD_PRIORITY_TIME_CRITICAL) {
176 value = THREAD_PRIORITY_TIME_CRITICAL;
177 } else {
178 value = THREAD_PRIORITY_NORMAL;
179 }
180 if (!SetThreadPriority(GetCurrentThread(), value)) {
181 return WIN_SetError("SetThreadPriority()");
182 }
183 return true;
184}
185
186void SDL_SYS_WaitThread(SDL_Thread *thread)
187{
188 WaitForSingleObjectEx(thread->handle, INFINITE, FALSE);
189 CloseHandle(thread->handle);
190}
191
192void SDL_SYS_DetachThread(SDL_Thread *thread)
193{
194 CloseHandle(thread->handle);
195}
196
197#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 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "SDL_internal.h"
22
23#ifndef SDL_systhread_c_h_
24#define SDL_systhread_c_h_
25
26#include "../../core/windows/SDL_windows.h"
27
28typedef HANDLE SYS_ThreadHandle;
29
30#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 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21
22#include "SDL_internal.h"
23
24#ifdef SDL_THREAD_WINDOWS
25
26#include "../../core/windows/SDL_windows.h"
27
28#include "../SDL_thread_c.h"
29
30static DWORD thread_local_storage = TLS_OUT_OF_INDEXES;
31static bool generic_local_storage = false;
32
33void SDL_SYS_InitTLSData(void)
34{
35 if (thread_local_storage == TLS_OUT_OF_INDEXES && !generic_local_storage) {
36 thread_local_storage = TlsAlloc();
37 if (thread_local_storage == TLS_OUT_OF_INDEXES) {
38 SDL_Generic_InitTLSData();
39 generic_local_storage = true;
40 }
41 }
42}
43
44SDL_TLSData *SDL_SYS_GetTLSData(void)
45{
46 if (generic_local_storage) {
47 return SDL_Generic_GetTLSData();
48 }
49
50 if (thread_local_storage != TLS_OUT_OF_INDEXES) {
51 return (SDL_TLSData *)TlsGetValue(thread_local_storage);
52 }
53 return NULL;
54}
55
56bool SDL_SYS_SetTLSData(SDL_TLSData *data)
57{
58 if (generic_local_storage) {
59 return SDL_Generic_SetTLSData(data);
60 }
61
62 if (!TlsSetValue(thread_local_storage, data)) {
63 return WIN_SetError("TlsSetValue()");
64 }
65 return true;
66}
67
68void SDL_SYS_QuitTLSData(void)
69{
70 if (generic_local_storage) {
71 SDL_Generic_QuitTLSData();
72 generic_local_storage = false;
73 } else {
74 if (thread_local_storage != TLS_OUT_OF_INDEXES) {
75 TlsFree(thread_local_storage);
76 thread_local_storage = TLS_OUT_OF_INDEXES;
77 }
78 }
79}
80
81#endif // SDL_THREAD_WINDOWS