summaryrefslogtreecommitdiff
path: root/contrib/SDL-3.2.8/src/thread
diff options
context:
space:
mode:
author3gg <3gg@shellblade.net>2025-12-27 12:03:39 -0800
committer3gg <3gg@shellblade.net>2025-12-27 12:03:39 -0800
commit5a079a2d114f96d4847d1ee305d5b7c16eeec50e (patch)
tree8926ab44f168acf787d8e19608857b3af0f82758 /contrib/SDL-3.2.8/src/thread
Initial commit
Diffstat (limited to 'contrib/SDL-3.2.8/src/thread')
-rw-r--r--contrib/SDL-3.2.8/src/thread/SDL_systhread.h77
-rw-r--r--contrib/SDL-3.2.8/src/thread/SDL_thread.c584
-rw-r--r--contrib/SDL-3.2.8/src/thread/SDL_thread_c.h92
-rw-r--r--contrib/SDL-3.2.8/src/thread/generic/SDL_syscond.c216
-rw-r--r--contrib/SDL-3.2.8/src/thread/generic/SDL_syscond_c.h36
-rw-r--r--contrib/SDL-3.2.8/src/thread/generic/SDL_sysmutex.c132
-rw-r--r--contrib/SDL-3.2.8/src/thread/generic/SDL_sysmutex_c.h21
-rw-r--r--contrib/SDL-3.2.8/src/thread/generic/SDL_sysrwlock.c194
-rw-r--r--contrib/SDL-3.2.8/src/thread/generic/SDL_sysrwlock_c.h38
-rw-r--r--contrib/SDL-3.2.8/src/thread/generic/SDL_syssem.c183
-rw-r--r--contrib/SDL-3.2.8/src/thread/generic/SDL_systhread.c57
-rw-r--r--contrib/SDL-3.2.8/src/thread/generic/SDL_systhread_c.h24
-rw-r--r--contrib/SDL-3.2.8/src/thread/generic/SDL_systls.c44
-rw-r--r--contrib/SDL-3.2.8/src/thread/n3ds/SDL_sysmutex.c67
-rw-r--r--contrib/SDL-3.2.8/src/thread/n3ds/SDL_sysmutex_c.h33
-rw-r--r--contrib/SDL-3.2.8/src/thread/n3ds/SDL_syssem.c112
-rw-r--r--contrib/SDL-3.2.8/src/thread/n3ds/SDL_systhread.c139
-rw-r--r--contrib/SDL-3.2.8/src/thread/n3ds/SDL_systhread_c.h30
-rw-r--r--contrib/SDL-3.2.8/src/thread/ps2/SDL_syssem.c122
-rw-r--r--contrib/SDL-3.2.8/src/thread/ps2/SDL_systhread.c143
-rw-r--r--contrib/SDL-3.2.8/src/thread/ps2/SDL_systhread_c.h22
-rw-r--r--contrib/SDL-3.2.8/src/thread/psp/SDL_sysmutex.c100
-rw-r--r--contrib/SDL-3.2.8/src/thread/psp/SDL_sysmutex_c.h21
-rw-r--r--contrib/SDL-3.2.8/src/thread/psp/SDL_syssem.c119
-rw-r--r--contrib/SDL-3.2.8/src/thread/psp/SDL_systhread.c112
-rw-r--r--contrib/SDL-3.2.8/src/thread/psp/SDL_systhread_c.h24
-rw-r--r--contrib/SDL-3.2.8/src/thread/pthread/SDL_syscond.c128
-rw-r--r--contrib/SDL-3.2.8/src/thread/pthread/SDL_sysmutex.c154
-rw-r--r--contrib/SDL-3.2.8/src/thread/pthread/SDL_sysmutex_c.h40
-rw-r--r--contrib/SDL-3.2.8/src/thread/pthread/SDL_sysrwlock.c113
-rw-r--r--contrib/SDL-3.2.8/src/thread/pthread/SDL_syssem.c160
-rw-r--r--contrib/SDL-3.2.8/src/thread/pthread/SDL_systhread.c293
-rw-r--r--contrib/SDL-3.2.8/src/thread/pthread/SDL_systhread_c.h25
-rw-r--r--contrib/SDL-3.2.8/src/thread/pthread/SDL_systls.c78
-rw-r--r--contrib/SDL-3.2.8/src/thread/vita/SDL_sysmutex.c97
-rw-r--r--contrib/SDL-3.2.8/src/thread/vita/SDL_sysmutex_c.h21
-rw-r--r--contrib/SDL-3.2.8/src/thread/vita/SDL_syssem.c122
-rw-r--r--contrib/SDL-3.2.8/src/thread/vita/SDL_systhread.c139
-rw-r--r--contrib/SDL-3.2.8/src/thread/vita/SDL_systhread_c.h24
-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
47 files changed, 5563 insertions, 0 deletions
diff --git a/contrib/SDL-3.2.8/src/thread/SDL_systhread.h b/contrib/SDL-3.2.8/src/thread/SDL_systhread.h
new file mode 100644
index 0000000..596b248
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/thread/SDL_systhread.h
@@ -0,0 +1,77 @@
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// These are functions that need to be implemented by a port of SDL
24
25#ifndef SDL_systhread_h_
26#define SDL_systhread_h_
27
28#include "SDL_thread_c.h"
29
30// Set up for C function definitions, even when using C++
31#ifdef __cplusplus
32extern "C" {
33#endif
34
35/* This function creates a thread, passing args to SDL_RunThread(),
36 saves a system-dependent thread id in thread->id, and returns 0
37 on success.
38*/
39extern bool SDL_SYS_CreateThread(SDL_Thread *thread,
40 SDL_FunctionPointer pfnBeginThread,
41 SDL_FunctionPointer pfnEndThread);
42
43// This function does any necessary setup in the child thread
44extern void SDL_SYS_SetupThread(const char *name);
45
46// This function sets the current thread priority
47extern bool SDL_SYS_SetThreadPriority(SDL_ThreadPriority priority);
48
49/* This function waits for the thread to finish and frees any data
50 allocated by SDL_SYS_CreateThread()
51 */
52extern void SDL_SYS_WaitThread(SDL_Thread *thread);
53
54// Mark thread as cleaned up as soon as it exits, without joining.
55extern void SDL_SYS_DetachThread(SDL_Thread *thread);
56
57// Initialize the global TLS data
58extern void SDL_SYS_InitTLSData(void);
59
60// Get the thread local storage for this thread
61extern SDL_TLSData *SDL_SYS_GetTLSData(void);
62
63// Set the thread local storage for this thread
64extern bool SDL_SYS_SetTLSData(SDL_TLSData *data);
65
66// Quit the global TLS data
67extern void SDL_SYS_QuitTLSData(void);
68
69// A helper function for setting up a thread with a stack size.
70extern SDL_Thread *SDL_CreateThreadWithStackSize(SDL_ThreadFunction fn, const char *name, size_t stacksize, void *data);
71
72// Ends C function definitions when using C++
73#ifdef __cplusplus
74}
75#endif
76
77#endif // SDL_systhread_h_
diff --git a/contrib/SDL-3.2.8/src/thread/SDL_thread.c b/contrib/SDL-3.2.8/src/thread/SDL_thread.c
new file mode 100644
index 0000000..99cb9dd
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/thread/SDL_thread.c
@@ -0,0 +1,584 @@
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// System independent thread management routines for SDL
24
25#include "SDL_thread_c.h"
26#include "SDL_systhread.h"
27#include "../SDL_error_c.h"
28
29// The storage is local to the thread, but the IDs are global for the process
30
31static SDL_AtomicInt SDL_tls_allocated;
32static SDL_AtomicInt SDL_tls_id;
33
34void SDL_InitTLSData(void)
35{
36 SDL_SYS_InitTLSData();
37}
38
39void *SDL_GetTLS(SDL_TLSID *id)
40{
41 SDL_TLSData *storage;
42 int storage_index;
43
44 if (id == NULL) {
45 SDL_InvalidParamError("id");
46 return NULL;
47 }
48
49 storage_index = SDL_GetAtomicInt(id) - 1;
50 storage = SDL_SYS_GetTLSData();
51 if (!storage || storage_index < 0 || storage_index >= storage->limit) {
52 return NULL;
53 }
54 return storage->array[storage_index].data;
55}
56
57bool SDL_SetTLS(SDL_TLSID *id, const void *value, SDL_TLSDestructorCallback destructor)
58{
59 SDL_TLSData *storage;
60 int storage_index;
61
62 if (id == NULL) {
63 return SDL_InvalidParamError("id");
64 }
65
66 /* Make sure TLS is initialized.
67 * There's a race condition here if you are calling this from non-SDL threads
68 * and haven't called SDL_Init() on your main thread, but such is life.
69 */
70 SDL_InitTLSData();
71
72 // Get the storage index associated with the ID in a thread-safe way
73 storage_index = SDL_GetAtomicInt(id) - 1;
74 if (storage_index < 0) {
75 int new_id = (SDL_AtomicIncRef(&SDL_tls_id) + 1);
76
77 SDL_CompareAndSwapAtomicInt(id, 0, new_id);
78
79 /* If there was a race condition we'll have wasted an ID, but every thread
80 * will have the same storage index for this id.
81 */
82 storage_index = SDL_GetAtomicInt(id) - 1;
83 }
84
85 // Get the storage for the current thread
86 storage = SDL_SYS_GetTLSData();
87 if (!storage || storage_index >= storage->limit) {
88 unsigned int i, oldlimit, newlimit;
89 SDL_TLSData *new_storage;
90
91 oldlimit = storage ? storage->limit : 0;
92 newlimit = (storage_index + TLS_ALLOC_CHUNKSIZE);
93 new_storage = (SDL_TLSData *)SDL_realloc(storage, sizeof(*storage) + (newlimit - 1) * sizeof(storage->array[0]));
94 if (!new_storage) {
95 return false;
96 }
97 storage = new_storage;
98 storage->limit = newlimit;
99 for (i = oldlimit; i < newlimit; ++i) {
100 storage->array[i].data = NULL;
101 storage->array[i].destructor = NULL;
102 }
103 if (!SDL_SYS_SetTLSData(storage)) {
104 SDL_free(storage);
105 return false;
106 }
107 SDL_AtomicIncRef(&SDL_tls_allocated);
108 }
109
110 storage->array[storage_index].data = SDL_const_cast(void *, value);
111 storage->array[storage_index].destructor = destructor;
112 return true;
113}
114
115void SDL_CleanupTLS(void)
116{
117 SDL_TLSData *storage;
118
119 // Cleanup the storage for the current thread
120 storage = SDL_SYS_GetTLSData();
121 if (storage) {
122 int i;
123 for (i = 0; i < storage->limit; ++i) {
124 if (storage->array[i].destructor) {
125 storage->array[i].destructor(storage->array[i].data);
126 }
127 }
128 SDL_SYS_SetTLSData(NULL);
129 SDL_free(storage);
130 (void)SDL_AtomicDecRef(&SDL_tls_allocated);
131 }
132}
133
134void SDL_QuitTLSData(void)
135{
136 SDL_CleanupTLS();
137
138 if (SDL_GetAtomicInt(&SDL_tls_allocated) == 0) {
139 SDL_SYS_QuitTLSData();
140 } else {
141 // Some thread hasn't called SDL_CleanupTLS()
142 }
143}
144
145/* This is a generic implementation of thread-local storage which doesn't
146 require additional OS support.
147
148 It is not especially efficient and doesn't clean up thread-local storage
149 as threads exit. If there is a real OS that doesn't support thread-local
150 storage this implementation should be improved to be production quality.
151*/
152
153typedef struct SDL_TLSEntry
154{
155 SDL_ThreadID thread;
156 SDL_TLSData *storage;
157 struct SDL_TLSEntry *next;
158} SDL_TLSEntry;
159
160static SDL_Mutex *SDL_generic_TLS_mutex;
161static SDL_TLSEntry *SDL_generic_TLS;
162
163void SDL_Generic_InitTLSData(void)
164{
165 if (!SDL_generic_TLS_mutex) {
166 SDL_generic_TLS_mutex = SDL_CreateMutex();
167 }
168}
169
170SDL_TLSData *SDL_Generic_GetTLSData(void)
171{
172 SDL_ThreadID thread = SDL_GetCurrentThreadID();
173 SDL_TLSEntry *entry;
174 SDL_TLSData *storage = NULL;
175
176 SDL_LockMutex(SDL_generic_TLS_mutex);
177 for (entry = SDL_generic_TLS; entry; entry = entry->next) {
178 if (entry->thread == thread) {
179 storage = entry->storage;
180 break;
181 }
182 }
183 SDL_UnlockMutex(SDL_generic_TLS_mutex);
184
185 return storage;
186}
187
188bool SDL_Generic_SetTLSData(SDL_TLSData *data)
189{
190 SDL_ThreadID thread = SDL_GetCurrentThreadID();
191 SDL_TLSEntry *prev, *entry;
192 bool result = true;
193
194 SDL_LockMutex(SDL_generic_TLS_mutex);
195 prev = NULL;
196 for (entry = SDL_generic_TLS; entry; entry = entry->next) {
197 if (entry->thread == thread) {
198 if (data) {
199 entry->storage = data;
200 } else {
201 if (prev) {
202 prev->next = entry->next;
203 } else {
204 SDL_generic_TLS = entry->next;
205 }
206 SDL_free(entry);
207 }
208 break;
209 }
210 prev = entry;
211 }
212 if (!entry && data) {
213 entry = (SDL_TLSEntry *)SDL_malloc(sizeof(*entry));
214 if (entry) {
215 entry->thread = thread;
216 entry->storage = data;
217 entry->next = SDL_generic_TLS;
218 SDL_generic_TLS = entry;
219 } else {
220 result = false;
221 }
222 }
223 SDL_UnlockMutex(SDL_generic_TLS_mutex);
224
225 return result;
226}
227
228void SDL_Generic_QuitTLSData(void)
229{
230 SDL_TLSEntry *entry;
231
232 // This should have been cleaned up by the time we get here
233 SDL_assert(!SDL_generic_TLS);
234 if (SDL_generic_TLS) {
235 SDL_LockMutex(SDL_generic_TLS_mutex);
236 for (entry = SDL_generic_TLS; entry; ) {
237 SDL_TLSEntry *next = entry->next;
238 SDL_free(entry->storage);
239 SDL_free(entry);
240 entry = next;
241 }
242 SDL_generic_TLS = NULL;
243 SDL_UnlockMutex(SDL_generic_TLS_mutex);
244 }
245
246 if (SDL_generic_TLS_mutex) {
247 SDL_DestroyMutex(SDL_generic_TLS_mutex);
248 SDL_generic_TLS_mutex = NULL;
249 }
250}
251
252// Non-thread-safe global error variable
253static SDL_error *SDL_GetStaticErrBuf(void)
254{
255 static SDL_error SDL_global_error;
256 static char SDL_global_error_str[128];
257 SDL_global_error.str = SDL_global_error_str;
258 SDL_global_error.len = sizeof(SDL_global_error_str);
259 return &SDL_global_error;
260}
261
262#ifndef SDL_THREADS_DISABLED
263static void SDLCALL SDL_FreeErrBuf(void *data)
264{
265 SDL_error *errbuf = (SDL_error *)data;
266
267 if (errbuf->str) {
268 errbuf->free_func(errbuf->str);
269 }
270 errbuf->free_func(errbuf);
271}
272#endif
273
274// Routine to get the thread-specific error variable
275SDL_error *SDL_GetErrBuf(bool create)
276{
277#ifdef SDL_THREADS_DISABLED
278 return SDL_GetStaticErrBuf();
279#else
280 static SDL_TLSID tls_errbuf;
281 SDL_error *errbuf;
282
283 errbuf = (SDL_error *)SDL_GetTLS(&tls_errbuf);
284 if (!errbuf) {
285 if (!create) {
286 return NULL;
287 }
288
289 /* Get the original memory functions for this allocation because the lifetime
290 * of the error buffer may span calls to SDL_SetMemoryFunctions() by the app
291 */
292 SDL_realloc_func realloc_func;
293 SDL_free_func free_func;
294 SDL_GetOriginalMemoryFunctions(NULL, NULL, &realloc_func, &free_func);
295
296 errbuf = (SDL_error *)realloc_func(NULL, sizeof(*errbuf));
297 if (!errbuf) {
298 return SDL_GetStaticErrBuf();
299 }
300 SDL_zerop(errbuf);
301 errbuf->realloc_func = realloc_func;
302 errbuf->free_func = free_func;
303 SDL_SetTLS(&tls_errbuf, errbuf, SDL_FreeErrBuf);
304 }
305 return errbuf;
306#endif // SDL_THREADS_DISABLED
307}
308
309static bool ThreadValid(SDL_Thread *thread)
310{
311 return SDL_ObjectValid(thread, SDL_OBJECT_TYPE_THREAD);
312}
313
314void SDL_RunThread(SDL_Thread *thread)
315{
316 void *userdata = thread->userdata;
317 int(SDLCALL *userfunc)(void *) = thread->userfunc;
318
319 int *statusloc = &thread->status;
320
321 // Perform any system-dependent setup - this function may not fail
322 SDL_SYS_SetupThread(thread->name);
323
324 // Get the thread id
325 thread->threadid = SDL_GetCurrentThreadID();
326
327 // Run the function
328 *statusloc = userfunc(userdata);
329
330 // Clean up thread-local storage
331 SDL_CleanupTLS();
332
333 // Mark us as ready to be joined (or detached)
334 if (!SDL_CompareAndSwapAtomicInt(&thread->state, SDL_THREAD_ALIVE, SDL_THREAD_COMPLETE)) {
335 // Clean up if something already detached us.
336 if (SDL_GetThreadState(thread) == SDL_THREAD_DETACHED) {
337 SDL_free(thread->name); // Can't free later, we've already cleaned up TLS
338 SDL_free(thread);
339 }
340 }
341}
342
343SDL_Thread *SDL_CreateThreadWithPropertiesRuntime(SDL_PropertiesID props,
344 SDL_FunctionPointer pfnBeginThread,
345 SDL_FunctionPointer pfnEndThread)
346{
347 // rather than check this in every backend, just make sure it's correct upfront. Only allow non-NULL if Windows, or Microsoft GDK.
348 #if !defined(SDL_PLATFORM_WINDOWS)
349 if (pfnBeginThread || pfnEndThread) {
350 SDL_SetError("_beginthreadex/_endthreadex not supported on this platform");
351 return NULL;
352 }
353 #endif
354
355 SDL_ThreadFunction fn = (SDL_ThreadFunction) SDL_GetPointerProperty(props, SDL_PROP_THREAD_CREATE_ENTRY_FUNCTION_POINTER, NULL);
356 const char *name = SDL_GetStringProperty(props, SDL_PROP_THREAD_CREATE_NAME_STRING, NULL);
357 const size_t stacksize = (size_t) SDL_GetNumberProperty(props, SDL_PROP_THREAD_CREATE_STACKSIZE_NUMBER, 0);
358 void *userdata = SDL_GetPointerProperty(props, SDL_PROP_THREAD_CREATE_USERDATA_POINTER, NULL);
359
360 if (!fn) {
361 SDL_SetError("Thread entry function is NULL");
362 return NULL;
363 }
364
365 SDL_InitMainThread();
366
367 SDL_Thread *thread = (SDL_Thread *)SDL_calloc(1, sizeof(*thread));
368 if (!thread) {
369 return NULL;
370 }
371 thread->status = -1;
372 SDL_SetAtomicInt(&thread->state, SDL_THREAD_ALIVE);
373
374 // Set up the arguments for the thread
375 if (name) {
376 thread->name = SDL_strdup(name);
377 if (!thread->name) {
378 SDL_free(thread);
379 return NULL;
380 }
381 }
382
383 thread->userfunc = fn;
384 thread->userdata = userdata;
385 thread->stacksize = stacksize;
386
387 SDL_SetObjectValid(thread, SDL_OBJECT_TYPE_THREAD, true);
388
389 // Create the thread and go!
390 if (!SDL_SYS_CreateThread(thread, pfnBeginThread, pfnEndThread)) {
391 // Oops, failed. Gotta free everything
392 SDL_SetObjectValid(thread, SDL_OBJECT_TYPE_THREAD, false);
393 SDL_free(thread->name);
394 SDL_free(thread);
395 thread = NULL;
396 }
397
398 // Everything is running now
399 return thread;
400}
401
402SDL_Thread *SDL_CreateThreadRuntime(SDL_ThreadFunction fn,
403 const char *name, void *userdata,
404 SDL_FunctionPointer pfnBeginThread,
405 SDL_FunctionPointer pfnEndThread)
406{
407 const SDL_PropertiesID props = SDL_CreateProperties();
408 SDL_SetPointerProperty(props, SDL_PROP_THREAD_CREATE_ENTRY_FUNCTION_POINTER, (void *) fn);
409 SDL_SetStringProperty(props, SDL_PROP_THREAD_CREATE_NAME_STRING, name);
410 SDL_SetPointerProperty(props, SDL_PROP_THREAD_CREATE_USERDATA_POINTER, userdata);
411 SDL_Thread *thread = SDL_CreateThreadWithPropertiesRuntime(props, pfnBeginThread, pfnEndThread);
412 SDL_DestroyProperties(props);
413 return thread;
414}
415
416// internal helper function, not in the public API.
417SDL_Thread *SDL_CreateThreadWithStackSize(SDL_ThreadFunction fn, const char *name, size_t stacksize, void *userdata)
418{
419 const SDL_PropertiesID props = SDL_CreateProperties();
420 SDL_SetPointerProperty(props, SDL_PROP_THREAD_CREATE_ENTRY_FUNCTION_POINTER, (void *) fn);
421 SDL_SetStringProperty(props, SDL_PROP_THREAD_CREATE_NAME_STRING, name);
422 SDL_SetPointerProperty(props, SDL_PROP_THREAD_CREATE_USERDATA_POINTER, userdata);
423 SDL_SetNumberProperty(props, SDL_PROP_THREAD_CREATE_STACKSIZE_NUMBER, (Sint64) stacksize);
424 SDL_Thread *thread = SDL_CreateThreadWithProperties(props);
425 SDL_DestroyProperties(props);
426 return thread;
427}
428
429SDL_ThreadID SDL_GetThreadID(SDL_Thread *thread)
430{
431 SDL_ThreadID id = 0;
432
433 if (thread) {
434 if (ThreadValid(thread)) {
435 id = thread->threadid;
436 }
437 } else {
438 id = SDL_GetCurrentThreadID();
439 }
440 return id;
441}
442
443const char *SDL_GetThreadName(SDL_Thread *thread)
444{
445 if (ThreadValid(thread)) {
446 return SDL_GetPersistentString(thread->name);
447 } else {
448 return NULL;
449 }
450}
451
452bool SDL_SetCurrentThreadPriority(SDL_ThreadPriority priority)
453{
454 return SDL_SYS_SetThreadPriority(priority);
455}
456
457void SDL_WaitThread(SDL_Thread *thread, int *status)
458{
459 if (!ThreadValid(thread)) {
460 if (status) {
461 *status = -1;
462 }
463 return;
464 }
465
466 SDL_SYS_WaitThread(thread);
467 if (status) {
468 *status = thread->status;
469 }
470 SDL_SetObjectValid(thread, SDL_OBJECT_TYPE_THREAD, false);
471 SDL_free(thread->name);
472 SDL_free(thread);
473}
474
475SDL_ThreadState SDL_GetThreadState(SDL_Thread *thread)
476{
477 if (!ThreadValid(thread)) {
478 return SDL_THREAD_UNKNOWN;
479 }
480
481 return (SDL_ThreadState)SDL_GetAtomicInt(&thread->state);
482}
483
484void SDL_DetachThread(SDL_Thread *thread)
485{
486 if (!ThreadValid(thread)) {
487 return;
488 }
489
490 // The thread may vanish at any time, it's no longer valid
491 SDL_SetObjectValid(thread, SDL_OBJECT_TYPE_THREAD, false);
492
493 // Grab dibs if the state is alive+joinable.
494 if (SDL_CompareAndSwapAtomicInt(&thread->state, SDL_THREAD_ALIVE, SDL_THREAD_DETACHED)) {
495 SDL_SYS_DetachThread(thread);
496 } else {
497 // all other states are pretty final, see where we landed.
498 SDL_ThreadState thread_state = SDL_GetThreadState(thread);
499 if (thread_state == SDL_THREAD_DETACHED) {
500 return; // already detached (you shouldn't call this twice!)
501 } else if (thread_state == SDL_THREAD_COMPLETE) {
502 SDL_WaitThread(thread, NULL); // already done, clean it up.
503 }
504 }
505}
506
507void SDL_WaitSemaphore(SDL_Semaphore *sem)
508{
509 SDL_WaitSemaphoreTimeoutNS(sem, -1);
510}
511
512bool SDL_TryWaitSemaphore(SDL_Semaphore *sem)
513{
514 return SDL_WaitSemaphoreTimeoutNS(sem, 0);
515}
516
517bool SDL_WaitSemaphoreTimeout(SDL_Semaphore *sem, Sint32 timeoutMS)
518{
519 Sint64 timeoutNS;
520
521 if (timeoutMS >= 0) {
522 timeoutNS = SDL_MS_TO_NS(timeoutMS);
523 } else {
524 timeoutNS = -1;
525 }
526 return SDL_WaitSemaphoreTimeoutNS(sem, timeoutNS);
527}
528
529void SDL_WaitCondition(SDL_Condition *cond, SDL_Mutex *mutex)
530{
531 SDL_WaitConditionTimeoutNS(cond, mutex, -1);
532}
533
534bool SDL_WaitConditionTimeout(SDL_Condition *cond, SDL_Mutex *mutex, Sint32 timeoutMS)
535{
536 Sint64 timeoutNS;
537
538 if (timeoutMS >= 0) {
539 timeoutNS = SDL_MS_TO_NS(timeoutMS);
540 } else {
541 timeoutNS = -1;
542 }
543 return SDL_WaitConditionTimeoutNS(cond, mutex, timeoutNS);
544}
545
546bool SDL_ShouldInit(SDL_InitState *state)
547{
548 while (SDL_GetAtomicInt(&state->status) != SDL_INIT_STATUS_INITIALIZED) {
549 if (SDL_CompareAndSwapAtomicInt(&state->status, SDL_INIT_STATUS_UNINITIALIZED, SDL_INIT_STATUS_INITIALIZING)) {
550 state->thread = SDL_GetCurrentThreadID();
551 return true;
552 }
553
554 // Wait for the other thread to complete transition
555 SDL_Delay(1);
556 }
557 return false;
558}
559
560bool SDL_ShouldQuit(SDL_InitState *state)
561{
562 while (SDL_GetAtomicInt(&state->status) != SDL_INIT_STATUS_UNINITIALIZED) {
563 if (SDL_CompareAndSwapAtomicInt(&state->status, SDL_INIT_STATUS_INITIALIZED, SDL_INIT_STATUS_UNINITIALIZING)) {
564 state->thread = SDL_GetCurrentThreadID();
565 return true;
566 }
567
568 // Wait for the other thread to complete transition
569 SDL_Delay(1);
570 }
571 return false;
572}
573
574void SDL_SetInitialized(SDL_InitState *state, bool initialized)
575{
576 SDL_assert(state->thread == SDL_GetCurrentThreadID());
577
578 if (initialized) {
579 SDL_SetAtomicInt(&state->status, SDL_INIT_STATUS_INITIALIZED);
580 } else {
581 SDL_SetAtomicInt(&state->status, SDL_INIT_STATUS_UNINITIALIZED);
582 }
583}
584
diff --git a/contrib/SDL-3.2.8/src/thread/SDL_thread_c.h b/contrib/SDL-3.2.8/src/thread/SDL_thread_c.h
new file mode 100644
index 0000000..3368016
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/thread/SDL_thread_c.h
@@ -0,0 +1,92 @@
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_thread_c_h_
24#define SDL_thread_c_h_
25
26// Need the definitions of SYS_ThreadHandle
27#ifdef SDL_THREADS_DISABLED
28#include "generic/SDL_systhread_c.h"
29#elif defined(SDL_THREAD_PTHREAD)
30#include "pthread/SDL_systhread_c.h"
31#elif defined(SDL_THREAD_WINDOWS)
32#include "windows/SDL_systhread_c.h"
33#elif defined(SDL_THREAD_PS2)
34#include "ps2/SDL_systhread_c.h"
35#elif defined(SDL_THREAD_PSP)
36#include "psp/SDL_systhread_c.h"
37#elif defined(SDL_THREAD_VITA)
38#include "vita/SDL_systhread_c.h"
39#elif defined(SDL_THREAD_N3DS)
40#include "n3ds/SDL_systhread_c.h"
41#else
42#error Need thread implementation for this platform
43#include "generic/SDL_systhread_c.h"
44#endif
45#include "../SDL_error_c.h"
46
47// This is the system-independent thread info structure
48struct SDL_Thread
49{
50 SDL_ThreadID threadid;
51 SYS_ThreadHandle handle;
52 int status;
53 SDL_AtomicInt state; /* SDL_ThreadState */
54 SDL_error errbuf;
55 char *name;
56 size_t stacksize; // 0 for default, >0 for user-specified stack size.
57 int(SDLCALL *userfunc)(void *);
58 void *userdata;
59 void *data;
60 SDL_FunctionPointer endfunc; // only used on some platforms.
61};
62
63// This is the function called to run a thread
64extern void SDL_RunThread(SDL_Thread *thread);
65
66// This is the system-independent thread local storage structure
67typedef struct
68{
69 int limit;
70 struct
71 {
72 void *data;
73 void(SDLCALL *destructor)(void *);
74 } array[1];
75} SDL_TLSData;
76
77// This is how many TLS entries we allocate at once
78#define TLS_ALLOC_CHUNKSIZE 4
79
80extern void SDL_InitTLSData(void);
81extern void SDL_QuitTLSData(void);
82
83/* Generic TLS support.
84 This is only intended as a fallback if getting real thread-local
85 storage fails or isn't supported on this platform.
86 */
87extern void SDL_Generic_InitTLSData(void);
88extern SDL_TLSData *SDL_Generic_GetTLSData(void);
89extern bool SDL_Generic_SetTLSData(SDL_TLSData *data);
90extern void SDL_Generic_QuitTLSData(void);
91
92#endif // SDL_thread_c_h_
diff --git a/contrib/SDL-3.2.8/src/thread/generic/SDL_syscond.c b/contrib/SDL-3.2.8/src/thread/generic/SDL_syscond.c
new file mode 100644
index 0000000..5fdfe84
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/thread/generic/SDL_syscond.c
@@ -0,0 +1,216 @@
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// An implementation of condition variables using semaphores and mutexes
24/*
25 This implementation borrows heavily from the BeOS condition variable
26 implementation, written by Christopher Tate and Owen Smith. Thanks!
27 */
28
29#include "../generic/SDL_syscond_c.h"
30
31/* If two implementations are to be compiled into SDL (the active one
32 * will be chosen at runtime), the function names need to be
33 * suffixed
34 */
35#ifndef SDL_THREAD_GENERIC_COND_SUFFIX
36#define SDL_CreateCondition_generic SDL_CreateCondition
37#define SDL_DestroyCondition_generic SDL_DestroyCondition
38#define SDL_SignalCondition_generic SDL_SignalCondition
39#define SDL_BroadcastCondition_generic SDL_BroadcastCondition
40#endif
41
42typedef struct SDL_cond_generic
43{
44 SDL_Semaphore *sem;
45 SDL_Semaphore *handshake_sem;
46 SDL_Semaphore *signal_sem;
47 int num_waiting;
48 int num_signals;
49} SDL_cond_generic;
50
51// Create a condition variable
52SDL_Condition *SDL_CreateCondition_generic(void)
53{
54 SDL_cond_generic *cond = (SDL_cond_generic *)SDL_calloc(1, sizeof(*cond));
55
56#ifndef SDL_THREADS_DISABLED
57 if (cond) {
58 cond->sem = SDL_CreateSemaphore(0);
59 cond->handshake_sem = SDL_CreateSemaphore(0);
60 cond->signal_sem = SDL_CreateSemaphore(1);
61 if (!cond->sem || !cond->handshake_sem || !cond->signal_sem) {
62 SDL_DestroyCondition_generic((SDL_Condition *)cond);
63 cond = NULL;
64 }
65 }
66#endif
67
68 return (SDL_Condition *)cond;
69}
70
71// Destroy a condition variable
72void SDL_DestroyCondition_generic(SDL_Condition *_cond)
73{
74 SDL_cond_generic *cond = (SDL_cond_generic *)_cond;
75 if (cond) {
76 if (cond->sem) {
77 SDL_DestroySemaphore(cond->sem);
78 }
79 if (cond->handshake_sem) {
80 SDL_DestroySemaphore(cond->handshake_sem);
81 }
82 if (cond->signal_sem) {
83 SDL_DestroySemaphore(cond->signal_sem);
84 }
85 SDL_free(cond);
86 }
87}
88
89// Restart one of the threads that are waiting on the condition variable
90void SDL_SignalCondition_generic(SDL_Condition *_cond)
91{
92 SDL_cond_generic *cond = (SDL_cond_generic *)_cond;
93 if (!cond) {
94 return;
95 }
96
97#ifndef SDL_THREADS_DISABLED
98 /* If there are waiting threads not already signalled, then
99 signal the condition and wait for the thread to respond.
100 */
101 SDL_WaitSemaphore(cond->signal_sem);
102 if (cond->num_waiting > cond->num_signals) {
103 cond->num_signals++;
104 SDL_SignalSemaphore(cond->sem);
105 SDL_SignalSemaphore(cond->signal_sem);
106 SDL_WaitSemaphore(cond->handshake_sem);
107 } else {
108 SDL_SignalSemaphore(cond->signal_sem);
109 }
110#endif
111}
112
113// Restart all threads that are waiting on the condition variable
114void SDL_BroadcastCondition_generic(SDL_Condition *_cond)
115{
116 SDL_cond_generic *cond = (SDL_cond_generic *)_cond;
117 if (!cond) {
118 return;
119 }
120
121#ifndef SDL_THREADS_DISABLED
122 /* If there are waiting threads not already signalled, then
123 signal the condition and wait for the thread to respond.
124 */
125 SDL_WaitSemaphore(cond->signal_sem);
126 if (cond->num_waiting > cond->num_signals) {
127 const int num_waiting = (cond->num_waiting - cond->num_signals);
128 cond->num_signals = cond->num_waiting;
129 for (int i = 0; i < num_waiting; i++) {
130 SDL_SignalSemaphore(cond->sem);
131 }
132 /* Now all released threads are blocked here, waiting for us.
133 Collect them all (and win fabulous prizes!) :-)
134 */
135 SDL_SignalSemaphore(cond->signal_sem);
136 for (int i = 0; i < num_waiting; i++) {
137 SDL_WaitSemaphore(cond->handshake_sem);
138 }
139 } else {
140 SDL_SignalSemaphore(cond->signal_sem);
141 }
142#endif
143}
144
145/* Wait on the condition variable for at most 'timeoutNS' nanoseconds.
146 The mutex must be locked before entering this function!
147 The mutex is unlocked during the wait, and locked again after the wait.
148
149Typical use:
150
151Thread A:
152 SDL_LockMutex(lock);
153 while ( ! condition ) {
154 SDL_WaitCondition(cond, lock);
155 }
156 SDL_UnlockMutex(lock);
157
158Thread B:
159 SDL_LockMutex(lock);
160 ...
161 condition = true;
162 ...
163 SDL_SignalCondition(cond);
164 SDL_UnlockMutex(lock);
165 */
166bool SDL_WaitConditionTimeoutNS_generic(SDL_Condition *_cond, SDL_Mutex *mutex, Sint64 timeoutNS)
167{
168 SDL_cond_generic *cond = (SDL_cond_generic *)_cond;
169 bool result = true;
170
171 if (!cond || !mutex) {
172 return true;
173 }
174
175#ifndef SDL_THREADS_DISABLED
176 /* Obtain the protection mutex, and increment the number of waiters.
177 This allows the signal mechanism to only perform a signal if there
178 are waiting threads.
179 */
180 SDL_WaitSemaphore(cond->signal_sem);
181 cond->num_waiting++;
182 SDL_SignalSemaphore(cond->signal_sem);
183
184 // Unlock the mutex, as is required by condition variable semantics
185 SDL_UnlockMutex(mutex);
186
187 // Wait for a signal
188 result = SDL_WaitSemaphoreTimeoutNS(cond->sem, timeoutNS);
189
190 /* Let the signaler know we have completed the wait, otherwise
191 the signaler can race ahead and get the condition semaphore
192 if we are stopped between the mutex unlock and semaphore wait,
193 giving a deadlock. See the following URL for details:
194 http://web.archive.org/web/20010914175514/http://www-classic.be.com/aboutbe/benewsletter/volume_III/Issue40.html#Workshop
195 */
196 SDL_WaitSemaphore(cond->signal_sem);
197 if (cond->num_signals > 0) {
198 SDL_SignalSemaphore(cond->handshake_sem);
199 cond->num_signals--;
200 }
201 cond->num_waiting--;
202 SDL_SignalSemaphore(cond->signal_sem);
203
204 // Lock the mutex, as is required by condition variable semantics
205 SDL_LockMutex(mutex);
206#endif
207
208 return result;
209}
210
211#ifndef SDL_THREAD_GENERIC_COND_SUFFIX
212bool SDL_WaitConditionTimeoutNS(SDL_Condition *cond, SDL_Mutex *mutex, Sint64 timeoutNS)
213{
214 return SDL_WaitConditionTimeoutNS_generic(cond, mutex, timeoutNS);
215}
216#endif
diff --git a/contrib/SDL-3.2.8/src/thread/generic/SDL_syscond_c.h b/contrib/SDL-3.2.8/src/thread/generic/SDL_syscond_c.h
new file mode 100644
index 0000000..253c394
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/thread/generic/SDL_syscond_c.h
@@ -0,0 +1,36 @@
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_syscond_generic_h_
24#define SDL_syscond_generic_h_
25
26#ifdef SDL_THREAD_GENERIC_COND_SUFFIX
27
28SDL_Condition *SDL_CreateCondition_generic(void);
29void SDL_DestroyCondition_generic(SDL_Condition *cond);
30void SDL_SignalCondition_generic(SDL_Condition *cond);
31void SDL_BroadcastCondition_generic(SDL_Condition *cond);
32bool SDL_WaitConditionTimeoutNS_generic(SDL_Condition *cond, SDL_Mutex *mutex, Sint64 timeoutNS);
33
34#endif // SDL_THREAD_GENERIC_COND_SUFFIX
35
36#endif // SDL_syscond_generic_h_
diff --git a/contrib/SDL-3.2.8/src/thread/generic/SDL_sysmutex.c b/contrib/SDL-3.2.8/src/thread/generic/SDL_sysmutex.c
new file mode 100644
index 0000000..e58da67
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/thread/generic/SDL_sysmutex.c
@@ -0,0 +1,132 @@
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// An implementation of mutexes using semaphores
24
25#include "SDL_systhread_c.h"
26
27struct SDL_Mutex
28{
29 int recursive;
30 SDL_ThreadID owner;
31 SDL_Semaphore *sem;
32};
33
34SDL_Mutex *SDL_CreateMutex(void)
35{
36 SDL_Mutex *mutex = (SDL_Mutex *)SDL_calloc(1, sizeof(*mutex));
37
38#ifndef SDL_THREADS_DISABLED
39 if (mutex) {
40 // Create the mutex semaphore, with initial value 1
41 mutex->sem = SDL_CreateSemaphore(1);
42 mutex->recursive = 0;
43 mutex->owner = 0;
44 if (!mutex->sem) {
45 SDL_free(mutex);
46 mutex = NULL;
47 }
48 }
49#endif // !SDL_THREADS_DISABLED
50
51 return mutex;
52}
53
54void SDL_DestroyMutex(SDL_Mutex *mutex)
55{
56 if (mutex) {
57 if (mutex->sem) {
58 SDL_DestroySemaphore(mutex->sem);
59 }
60 SDL_free(mutex);
61 }
62}
63
64void SDL_LockMutex(SDL_Mutex *mutex) SDL_NO_THREAD_SAFETY_ANALYSIS // clang doesn't know about NULL mutexes
65{
66#ifndef SDL_THREADS_DISABLED
67 if (mutex != NULL) {
68 SDL_ThreadID this_thread = SDL_GetCurrentThreadID();
69 if (mutex->owner == this_thread) {
70 ++mutex->recursive;
71 } else {
72 /* The order of operations is important.
73 We set the locking thread id after we obtain the lock
74 so unlocks from other threads will fail.
75 */
76 SDL_WaitSemaphore(mutex->sem);
77 mutex->owner = this_thread;
78 mutex->recursive = 0;
79 }
80 }
81#endif // SDL_THREADS_DISABLED
82}
83
84bool SDL_TryLockMutex(SDL_Mutex *mutex)
85{
86 bool result = true;
87#ifndef SDL_THREADS_DISABLED
88 if (mutex) {
89 SDL_ThreadID this_thread = SDL_GetCurrentThreadID();
90 if (mutex->owner == this_thread) {
91 ++mutex->recursive;
92 } else {
93 /* The order of operations is important.
94 We set the locking thread id after we obtain the lock
95 so unlocks from other threads will fail.
96 */
97 result = SDL_TryWaitSemaphore(mutex->sem);
98 if (result) {
99 mutex->owner = this_thread;
100 mutex->recursive = 0;
101 }
102 }
103 }
104#endif // SDL_THREADS_DISABLED
105 return result;
106}
107
108void SDL_UnlockMutex(SDL_Mutex *mutex) SDL_NO_THREAD_SAFETY_ANALYSIS // clang doesn't know about NULL mutexes
109{
110#ifndef SDL_THREADS_DISABLED
111 if (mutex != NULL) {
112 // If we don't own the mutex, we can't unlock it
113 if (SDL_GetCurrentThreadID() != mutex->owner) {
114 SDL_assert(!"Tried to unlock a mutex we don't own!");
115 return; // (undefined behavior!) SDL_SetError("mutex not owned by this thread");
116 }
117
118 if (mutex->recursive) {
119 --mutex->recursive;
120 } else {
121 /* The order of operations is important.
122 First reset the owner so another thread doesn't lock
123 the mutex and set the ownership before we reset it,
124 then release the lock semaphore.
125 */
126 mutex->owner = 0;
127 SDL_SignalSemaphore(mutex->sem);
128 }
129 }
130#endif // SDL_THREADS_DISABLED
131}
132
diff --git a/contrib/SDL-3.2.8/src/thread/generic/SDL_sysmutex_c.h b/contrib/SDL-3.2.8/src/thread/generic/SDL_sysmutex_c.h
new file mode 100644
index 0000000..4b0c6f8
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/thread/generic/SDL_sysmutex_c.h
@@ -0,0 +1,21 @@
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"
diff --git a/contrib/SDL-3.2.8/src/thread/generic/SDL_sysrwlock.c b/contrib/SDL-3.2.8/src/thread/generic/SDL_sysrwlock.c
new file mode 100644
index 0000000..24caf51
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/thread/generic/SDL_sysrwlock.c
@@ -0,0 +1,194 @@
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// An implementation of rwlocks using mutexes, condition variables, and atomics.
24
25#include "SDL_systhread_c.h"
26
27#include "../generic/SDL_sysrwlock_c.h"
28
29/* If two implementations are to be compiled into SDL (the active one
30 * will be chosen at runtime), the function names need to be
31 * suffixed
32 */
33// !!! FIXME: this is quite a tapdance with macros and the build system, maybe we can simplify how we do this. --ryan.
34#ifndef SDL_THREAD_GENERIC_RWLOCK_SUFFIX
35#define SDL_CreateRWLock_generic SDL_CreateRWLock
36#define SDL_DestroyRWLock_generic SDL_DestroyRWLock
37#define SDL_LockRWLockForReading_generic SDL_LockRWLockForReading
38#define SDL_LockRWLockForWriting_generic SDL_LockRWLockForWriting
39#define SDL_UnlockRWLock_generic SDL_UnlockRWLock
40#endif
41
42struct SDL_RWLock
43{
44#ifdef SDL_THREADS_DISABLED
45 int unused;
46#else
47 SDL_Mutex *lock;
48 SDL_Condition *condition;
49 SDL_ThreadID writer_thread;
50 SDL_AtomicInt reader_count;
51 SDL_AtomicInt writer_count;
52#endif
53};
54
55SDL_RWLock *SDL_CreateRWLock_generic(void)
56{
57 SDL_RWLock *rwlock = (SDL_RWLock *) SDL_calloc(1, sizeof (*rwlock));
58
59 if (!rwlock) {
60 return NULL;
61 }
62
63#ifndef SDL_THREADS_DISABLED
64 rwlock->lock = SDL_CreateMutex();
65 if (!rwlock->lock) {
66 SDL_free(rwlock);
67 return NULL;
68 }
69
70 rwlock->condition = SDL_CreateCondition();
71 if (!rwlock->condition) {
72 SDL_DestroyMutex(rwlock->lock);
73 SDL_free(rwlock);
74 return NULL;
75 }
76
77 SDL_SetAtomicInt(&rwlock->reader_count, 0);
78 SDL_SetAtomicInt(&rwlock->writer_count, 0);
79#endif
80
81 return rwlock;
82}
83
84void SDL_DestroyRWLock_generic(SDL_RWLock *rwlock)
85{
86 if (rwlock) {
87#ifndef SDL_THREADS_DISABLED
88 SDL_DestroyMutex(rwlock->lock);
89 SDL_DestroyCondition(rwlock->condition);
90#endif
91 SDL_free(rwlock);
92 }
93}
94
95void SDL_LockRWLockForReading_generic(SDL_RWLock *rwlock) SDL_NO_THREAD_SAFETY_ANALYSIS // clang doesn't know about NULL mutexes
96{
97#ifndef SDL_THREADS_DISABLED
98 if (rwlock) {
99 // !!! FIXME: these don't have to be atomic, we always gate them behind a mutex.
100 SDL_LockMutex(rwlock->lock);
101 SDL_assert(SDL_GetAtomicInt(&rwlock->writer_count) == 0); // shouldn't be able to grab lock if there's a writer!
102 SDL_AddAtomicInt(&rwlock->reader_count, 1);
103 SDL_UnlockMutex(rwlock->lock); // other readers can attempt to share the lock.
104 }
105#endif
106}
107
108void SDL_LockRWLockForWriting_generic(SDL_RWLock *rwlock) SDL_NO_THREAD_SAFETY_ANALYSIS // clang doesn't know about NULL mutexes
109{
110#ifndef SDL_THREADS_DISABLED
111 if (rwlock) {
112 SDL_LockMutex(rwlock->lock);
113 while (SDL_GetAtomicInt(&rwlock->reader_count) > 0) { // while something is holding the shared lock, keep waiting.
114 SDL_WaitCondition(rwlock->condition, rwlock->lock); // release the lock and wait for readers holding the shared lock to release it, regrab the lock.
115 }
116
117 // we hold the lock!
118 SDL_AddAtomicInt(&rwlock->writer_count, 1); // we let these be recursive, but the API doesn't require this. It _does_ trust you unlock correctly!
119 }
120#endif
121}
122
123bool SDL_TryLockRWLockForReading_generic(SDL_RWLock *rwlock)
124{
125#ifndef SDL_THREADS_DISABLED
126 if (rwlock) {
127 if (!SDL_TryLockMutex(rwlock->lock)) {
128 // !!! FIXME: there is a small window where a reader has to lock the mutex, and if we hit that, we will return SDL_RWLOCK_TIMEDOUT even though we could have shared the lock.
129 return false;
130 }
131
132 SDL_assert(SDL_GetAtomicInt(&rwlock->writer_count) == 0); // shouldn't be able to grab lock if there's a writer!
133 SDL_AddAtomicInt(&rwlock->reader_count, 1);
134 SDL_UnlockMutex(rwlock->lock); // other readers can attempt to share the lock.
135 }
136#endif
137
138 return true;
139}
140
141#ifndef SDL_THREAD_GENERIC_RWLOCK_SUFFIX
142bool SDL_TryLockRWLockForReading(SDL_RWLock *rwlock)
143{
144 return SDL_TryLockRWLockForReading_generic(rwlock);
145}
146#endif
147
148bool SDL_TryLockRWLockForWriting_generic(SDL_RWLock *rwlock)
149{
150#ifndef SDL_THREADS_DISABLED
151 if (rwlock) {
152 if (!SDL_TryLockMutex(rwlock->lock)) {
153 return false;
154 }
155
156 if (SDL_GetAtomicInt(&rwlock->reader_count) > 0) { // a reader is using the shared lock, treat it as unavailable.
157 SDL_UnlockMutex(rwlock->lock);
158 return false;
159 }
160
161 // we hold the lock!
162 SDL_AddAtomicInt(&rwlock->writer_count, 1); // we let these be recursive, but the API doesn't require this. It _does_ trust you unlock correctly!
163 }
164#endif
165
166 return true;
167}
168
169#ifndef SDL_THREAD_GENERIC_RWLOCK_SUFFIX
170bool SDL_TryLockRWLockForWriting(SDL_RWLock *rwlock)
171{
172 return SDL_TryLockRWLockForWriting_generic(rwlock);
173}
174#endif
175
176void SDL_UnlockRWLock_generic(SDL_RWLock *rwlock) SDL_NO_THREAD_SAFETY_ANALYSIS // clang doesn't know about NULL mutexes
177{
178#ifndef SDL_THREADS_DISABLED
179 if (rwlock) {
180 SDL_LockMutex(rwlock->lock); // recursive lock for writers, readers grab lock to make sure things are sane.
181
182 if (SDL_GetAtomicInt(&rwlock->reader_count) > 0) { // we're a reader
183 SDL_AddAtomicInt(&rwlock->reader_count, -1);
184 SDL_BroadcastCondition(rwlock->condition); // alert any pending writers to attempt to try to grab the lock again.
185 } else if (SDL_GetAtomicInt(&rwlock->writer_count) > 0) { // we're a writer
186 SDL_AddAtomicInt(&rwlock->writer_count, -1);
187 SDL_UnlockMutex(rwlock->lock); // recursive unlock.
188 }
189
190 SDL_UnlockMutex(rwlock->lock);
191 }
192#endif
193}
194
diff --git a/contrib/SDL-3.2.8/src/thread/generic/SDL_sysrwlock_c.h b/contrib/SDL-3.2.8/src/thread/generic/SDL_sysrwlock_c.h
new file mode 100644
index 0000000..9589d25
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/thread/generic/SDL_sysrwlock_c.h
@@ -0,0 +1,38 @@
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_sysrwlock_c_h_
24#define SDL_sysrwlock_c_h_
25
26#ifdef SDL_THREAD_GENERIC_RWLOCK_SUFFIX
27
28SDL_RWLock *SDL_CreateRWLock_generic(void);
29void SDL_DestroyRWLock_generic(SDL_RWLock *rwlock);
30void SDL_LockRWLockForReading_generic(SDL_RWLock *rwlock);
31void SDL_LockRWLockForWriting_generic(SDL_RWLock *rwlock);
32bool SDL_TryLockRWLockForReading_generic(SDL_RWLock *rwlock);
33bool SDL_TryLockRWLockForWriting_generic(SDL_RWLock *rwlock);
34void SDL_UnlockRWLock_generic(SDL_RWLock *rwlock);
35
36#endif // SDL_THREAD_GENERIC_RWLOCK_SUFFIX
37
38#endif // SDL_sysrwlock_c_h_
diff --git a/contrib/SDL-3.2.8/src/thread/generic/SDL_syssem.c b/contrib/SDL-3.2.8/src/thread/generic/SDL_syssem.c
new file mode 100644
index 0000000..dcd6fcc
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/thread/generic/SDL_syssem.c
@@ -0,0 +1,183 @@
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// An implementation of semaphores using mutexes and condition variables
24
25#include "SDL_systhread_c.h"
26
27#ifdef SDL_THREADS_DISABLED
28
29SDL_Semaphore *SDL_CreateSemaphore(Uint32 initial_value)
30{
31 SDL_SetError("SDL not built with thread support");
32 return NULL;
33}
34
35void SDL_DestroySemaphore(SDL_Semaphore *sem)
36{
37}
38
39bool SDL_WaitSemaphoreTimeoutNS(SDL_Semaphore *sem, Sint64 timeoutNS)
40{
41 return true;
42}
43
44Uint32 SDL_GetSemaphoreValue(SDL_Semaphore *sem)
45{
46 return 0;
47}
48
49void SDL_SignalSemaphore(SDL_Semaphore *sem)
50{
51 return;
52}
53
54#else
55
56struct SDL_Semaphore
57{
58 Uint32 count;
59 Uint32 waiters_count;
60 SDL_Mutex *count_lock;
61 SDL_Condition *count_nonzero;
62};
63
64SDL_Semaphore *SDL_CreateSemaphore(Uint32 initial_value)
65{
66 SDL_Semaphore *sem;
67
68 sem = (SDL_Semaphore *)SDL_malloc(sizeof(*sem));
69 if (!sem) {
70 return NULL;
71 }
72 sem->count = initial_value;
73 sem->waiters_count = 0;
74
75 sem->count_lock = SDL_CreateMutex();
76 sem->count_nonzero = SDL_CreateCondition();
77 if (!sem->count_lock || !sem->count_nonzero) {
78 SDL_DestroySemaphore(sem);
79 return NULL;
80 }
81
82 return sem;
83}
84
85/* WARNING:
86 You cannot call this function when another thread is using the semaphore.
87*/
88void SDL_DestroySemaphore(SDL_Semaphore *sem)
89{
90 if (sem) {
91 sem->count = 0xFFFFFFFF;
92 while (sem->waiters_count > 0) {
93 SDL_SignalCondition(sem->count_nonzero);
94 SDL_Delay(10);
95 }
96 SDL_DestroyCondition(sem->count_nonzero);
97 if (sem->count_lock) {
98 SDL_LockMutex(sem->count_lock);
99 SDL_UnlockMutex(sem->count_lock);
100 SDL_DestroyMutex(sem->count_lock);
101 }
102 SDL_free(sem);
103 }
104}
105
106bool SDL_WaitSemaphoreTimeoutNS(SDL_Semaphore *sem, Sint64 timeoutNS)
107{
108 bool result = false;
109
110 if (!sem) {
111 return true;
112 }
113
114 if (timeoutNS == 0) {
115 SDL_LockMutex(sem->count_lock);
116 if (sem->count > 0) {
117 --sem->count;
118 result = true;
119 }
120 SDL_UnlockMutex(sem->count_lock);
121 } else if (timeoutNS < 0) {
122 SDL_LockMutex(sem->count_lock);
123 ++sem->waiters_count;
124 while (sem->count == 0) {
125 SDL_WaitConditionTimeoutNS(sem->count_nonzero, sem->count_lock, -1);
126 }
127 --sem->waiters_count;
128 --sem->count;
129 result = true;
130 SDL_UnlockMutex(sem->count_lock);
131 } else {
132 Uint64 stop_time = SDL_GetTicksNS() + timeoutNS;
133
134 SDL_LockMutex(sem->count_lock);
135 ++sem->waiters_count;
136 while (sem->count == 0) {
137 Sint64 waitNS = (Sint64)(stop_time - SDL_GetTicksNS());
138 if (waitNS > 0) {
139 SDL_WaitConditionTimeoutNS(sem->count_nonzero, sem->count_lock, waitNS);
140 } else {
141 break;
142 }
143 }
144 --sem->waiters_count;
145
146 if (sem->count > 0) {
147 --sem->count;
148 result = true;
149 }
150 SDL_UnlockMutex(sem->count_lock);
151 }
152
153 return result;
154}
155
156Uint32 SDL_GetSemaphoreValue(SDL_Semaphore *sem)
157{
158 Uint32 value;
159
160 value = 0;
161 if (sem) {
162 SDL_LockMutex(sem->count_lock);
163 value = sem->count;
164 SDL_UnlockMutex(sem->count_lock);
165 }
166 return value;
167}
168
169void SDL_SignalSemaphore(SDL_Semaphore *sem)
170{
171 if (!sem) {
172 return;
173 }
174
175 SDL_LockMutex(sem->count_lock);
176 if (sem->waiters_count > 0) {
177 SDL_SignalCondition(sem->count_nonzero);
178 }
179 ++sem->count;
180 SDL_UnlockMutex(sem->count_lock);
181}
182
183#endif // SDL_THREADS_DISABLED
diff --git a/contrib/SDL-3.2.8/src/thread/generic/SDL_systhread.c b/contrib/SDL-3.2.8/src/thread/generic/SDL_systhread.c
new file mode 100644
index 0000000..ecfa4e1
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/thread/generic/SDL_systhread.c
@@ -0,0 +1,57 @@
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// Thread management routines for SDL
24
25#include "../SDL_systhread.h"
26
27bool SDL_SYS_CreateThread(SDL_Thread *thread,
28 SDL_FunctionPointer pfnBeginThread,
29 SDL_FunctionPointer pfnEndThread)
30{
31 return SDL_SetError("Threads are not supported on this platform");
32}
33
34void SDL_SYS_SetupThread(const char *name)
35{
36 return;
37}
38
39SDL_ThreadID SDL_GetCurrentThreadID(void)
40{
41 return 0;
42}
43
44bool SDL_SYS_SetThreadPriority(SDL_ThreadPriority priority)
45{
46 return true;
47}
48
49void SDL_SYS_WaitThread(SDL_Thread *thread)
50{
51 return;
52}
53
54void SDL_SYS_DetachThread(SDL_Thread *thread)
55{
56 return;
57}
diff --git a/contrib/SDL-3.2.8/src/thread/generic/SDL_systhread_c.h b/contrib/SDL-3.2.8/src/thread/generic/SDL_systhread_c.h
new file mode 100644
index 0000000..9ba55fd
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/thread/generic/SDL_systhread_c.h
@@ -0,0 +1,24 @@
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// Stub until we implement threads on this platform
24typedef int SYS_ThreadHandle;
diff --git a/contrib/SDL-3.2.8/src/thread/generic/SDL_systls.c b/contrib/SDL-3.2.8/src/thread/generic/SDL_systls.c
new file mode 100644
index 0000000..b8ebafe
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/thread/generic/SDL_systls.c
@@ -0,0 +1,44 @@
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#include "../SDL_thread_c.h"
24
25void SDL_SYS_InitTLSData(void)
26{
27 SDL_Generic_InitTLSData();
28}
29
30SDL_TLSData *SDL_SYS_GetTLSData(void)
31{
32 return SDL_Generic_GetTLSData();
33}
34
35bool SDL_SYS_SetTLSData(SDL_TLSData *data)
36{
37 return SDL_Generic_SetTLSData(data);
38}
39
40void SDL_SYS_QuitTLSData(void)
41{
42 SDL_Generic_QuitTLSData();
43}
44
diff --git a/contrib/SDL-3.2.8/src/thread/n3ds/SDL_sysmutex.c b/contrib/SDL-3.2.8/src/thread/n3ds/SDL_sysmutex.c
new file mode 100644
index 0000000..0abb9f5
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/thread/n3ds/SDL_sysmutex.c
@@ -0,0 +1,67 @@
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_N3DS
24
25// An implementation of mutexes using libctru's RecursiveLock
26
27#include "SDL_sysmutex_c.h"
28
29SDL_Mutex *SDL_CreateMutex(void)
30{
31 SDL_Mutex *mutex = (SDL_Mutex *)SDL_malloc(sizeof(*mutex));
32 if (mutex) {
33 RecursiveLock_Init(&mutex->lock);
34 }
35 return mutex;
36}
37
38void SDL_DestroyMutex(SDL_Mutex *mutex)
39{
40 if (mutex) {
41 SDL_free(mutex);
42 }
43}
44
45void SDL_LockMutex(SDL_Mutex *mutex) SDL_NO_THREAD_SAFETY_ANALYSIS // clang doesn't know about NULL mutexes
46{
47 if (mutex) {
48 RecursiveLock_Lock(&mutex->lock);
49 }
50}
51
52bool SDL_TryLockMutex(SDL_Mutex *mutex)
53{
54 if (mutex) {
55 return RecursiveLock_TryLock(&mutex->lock);
56 }
57 return true;
58}
59
60void SDL_UnlockMutex(SDL_Mutex *mutex) SDL_NO_THREAD_SAFETY_ANALYSIS // clang doesn't know about NULL mutexes
61{
62 if (mutex) {
63 RecursiveLock_Unlock(&mutex->lock);
64 }
65}
66
67#endif // SDL_THREAD_N3DS
diff --git a/contrib/SDL-3.2.8/src/thread/n3ds/SDL_sysmutex_c.h b/contrib/SDL-3.2.8/src/thread/n3ds/SDL_sysmutex_c.h
new file mode 100644
index 0000000..ddf8069
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/thread/n3ds/SDL_sysmutex_c.h
@@ -0,0 +1,33 @@
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_sysmutex_c_h_
24#define SDL_sysmutex_c_h_
25
26#include <3ds.h>
27
28struct SDL_Mutex
29{
30 RecursiveLock lock;
31};
32
33#endif // SDL_sysmutex_c_h
diff --git a/contrib/SDL-3.2.8/src/thread/n3ds/SDL_syssem.c b/contrib/SDL-3.2.8/src/thread/n3ds/SDL_syssem.c
new file mode 100644
index 0000000..3207a2d
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/thread/n3ds/SDL_syssem.c
@@ -0,0 +1,112 @@
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_N3DS
24
25// An implementation of semaphores using libctru's LightSemaphore
26
27#include <3ds.h>
28
29struct SDL_Semaphore
30{
31 LightSemaphore semaphore;
32};
33
34SDL_Semaphore *SDL_CreateSemaphore(Uint32 initial_value)
35{
36 SDL_Semaphore *sem;
37
38 if (initial_value > SDL_MAX_SINT16) {
39 SDL_SetError("Initial semaphore value too high for this platform");
40 return NULL;
41 }
42
43 sem = (SDL_Semaphore *)SDL_malloc(sizeof(*sem));
44 if (!sem) {
45 return NULL;
46 }
47
48 LightSemaphore_Init(&sem->semaphore, initial_value, SDL_MAX_SINT16);
49
50 return sem;
51}
52
53/* WARNING:
54 You cannot call this function when another thread is using the semaphore.
55*/
56void SDL_DestroySemaphore(SDL_Semaphore *sem)
57{
58 SDL_free(sem);
59}
60
61static bool WaitOnSemaphoreFor(SDL_Semaphore *sem, Sint64 timeoutNS)
62{
63 Uint64 stop_time = SDL_GetTicksNS() + timeoutNS;
64 while (SDL_GetTicksNS() < stop_time) {
65 if (LightSemaphore_TryAcquire(&sem->semaphore, 1) == 0) {
66 return true;
67 }
68 // 100 microseconds seems to be the sweet spot
69 SDL_DelayNS(SDL_US_TO_NS(100));
70 }
71
72 // If we failed, yield to avoid starvation on busy waits
73 SDL_DelayNS(1);
74 return false;
75}
76
77bool SDL_WaitSemaphoreTimeoutNS(SDL_Semaphore *sem, Sint64 timeoutNS)
78{
79 if (!sem) {
80 return true;
81 }
82
83 if (timeoutNS < 0) { // -1 == wait indefinitely.
84 LightSemaphore_Acquire(&sem->semaphore, 1);
85 return true;
86 }
87
88 if (LightSemaphore_TryAcquire(&sem->semaphore, 1) == 0) {
89 return true;
90 }
91
92 return WaitOnSemaphoreFor(sem, timeoutNS);
93}
94
95Uint32 SDL_GetSemaphoreValue(SDL_Semaphore *sem)
96{
97 if (!sem) {
98 return 0;
99 }
100 return sem->semaphore.current_count;
101}
102
103void SDL_SignalSemaphore(SDL_Semaphore *sem)
104{
105 if (sem) {
106 return;
107 }
108
109 LightSemaphore_Release(&sem->semaphore, 1);
110}
111
112#endif // SDL_THREAD_N3DS
diff --git a/contrib/SDL-3.2.8/src/thread/n3ds/SDL_systhread.c b/contrib/SDL-3.2.8/src/thread/n3ds/SDL_systhread.c
new file mode 100644
index 0000000..6bd826b
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/thread/n3ds/SDL_systhread.c
@@ -0,0 +1,139 @@
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_N3DS
24
25// Thread management routines for SDL
26
27#include "../SDL_systhread.h"
28
29// N3DS has very limited RAM (128MB), so we set a low default thread stack size.
30#define N3DS_THREAD_STACK_SIZE_DEFAULT (80 * 1024)
31
32#define N3DS_THREAD_PRIORITY_LOW 0x3F /**< Minimum priority */
33#define N3DS_THREAD_PRIORITY_MEDIUM 0x2F /**< Slightly higher than main thread (0x30) */
34#define N3DS_THREAD_PRIORITY_HIGH 0x19 /**< High priority for non-video work */
35#define N3DS_THREAD_PRIORITY_TIME_CRITICAL 0x18 /**< Highest priority */
36
37static size_t GetStackSize(size_t requested_size);
38
39static void ThreadEntry(void *arg)
40{
41 SDL_RunThread((SDL_Thread *)arg);
42 threadExit(0);
43}
44
45
46bool SDL_SYS_CreateThread(SDL_Thread *thread,
47 SDL_FunctionPointer pfnBeginThread,
48 SDL_FunctionPointer pfnEndThread)
49{
50 s32 priority = 0x30;
51 int cpu = -1;
52 size_t stack_size = GetStackSize(thread->stacksize);
53
54 svcGetThreadPriority(&priority, CUR_THREAD_HANDLE);
55
56 // prefer putting audio thread on system core
57 if (thread->name && (SDL_strncmp(thread->name, "SDLAudioP", 9) == 0) && R_SUCCEEDED(APT_SetAppCpuTimeLimit(30))) {
58 cpu = 1;
59 }
60
61 thread->handle = threadCreate(ThreadEntry,
62 thread,
63 stack_size,
64 priority,
65 cpu,
66 false);
67
68 if (!thread->handle) {
69 return SDL_SetError("Couldn't create thread");
70 }
71
72 return true;
73}
74
75static size_t GetStackSize(size_t requested_size)
76{
77 if (requested_size == 0) {
78 return N3DS_THREAD_STACK_SIZE_DEFAULT;
79 }
80
81 return requested_size;
82}
83
84void SDL_SYS_SetupThread(const char *name)
85{
86 return;
87}
88
89SDL_ThreadID SDL_GetCurrentThreadID(void)
90{
91 u32 thread_ID = 0;
92 svcGetThreadId(&thread_ID, CUR_THREAD_HANDLE);
93 return (SDL_ThreadID)thread_ID;
94}
95
96bool SDL_SYS_SetThreadPriority(SDL_ThreadPriority sdl_priority)
97{
98 s32 svc_priority;
99 switch (sdl_priority) {
100 case SDL_THREAD_PRIORITY_LOW:
101 svc_priority = N3DS_THREAD_PRIORITY_LOW;
102 break;
103 case SDL_THREAD_PRIORITY_NORMAL:
104 svc_priority = N3DS_THREAD_PRIORITY_MEDIUM;
105 break;
106 case SDL_THREAD_PRIORITY_HIGH:
107 svc_priority = N3DS_THREAD_PRIORITY_HIGH;
108 break;
109 case SDL_THREAD_PRIORITY_TIME_CRITICAL:
110 svc_priority = N3DS_THREAD_PRIORITY_TIME_CRITICAL;
111 break;
112 default:
113 svc_priority = N3DS_THREAD_PRIORITY_MEDIUM;
114 }
115 if (svcSetThreadPriority(CUR_THREAD_HANDLE, svc_priority) < 0) {
116 return SDL_SetError("svcSetThreadPriority failed");
117 }
118 return true;
119}
120
121void SDL_SYS_WaitThread(SDL_Thread *thread)
122{
123 Result res = threadJoin(thread->handle, U64_MAX);
124
125 /*
126 Detached threads can be waited on, but should NOT be cleaned manually
127 as it would result in a fatal error.
128 */
129 if (R_SUCCEEDED(res) && SDL_GetThreadState(thread) != SDL_THREAD_DETACHED) {
130 threadFree(thread->handle);
131 }
132}
133
134void SDL_SYS_DetachThread(SDL_Thread *thread)
135{
136 threadDetach(thread->handle);
137}
138
139#endif // SDL_THREAD_N3DS
diff --git a/contrib/SDL-3.2.8/src/thread/n3ds/SDL_systhread_c.h b/contrib/SDL-3.2.8/src/thread/n3ds/SDL_systhread_c.h
new file mode 100644
index 0000000..d87d636
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/thread/n3ds/SDL_systhread_c.h
@@ -0,0 +1,30 @@
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 <3ds.h>
27
28typedef Thread SYS_ThreadHandle;
29
30#endif // SDL_systhread_c_h_
diff --git a/contrib/SDL-3.2.8/src/thread/ps2/SDL_syssem.c b/contrib/SDL-3.2.8/src/thread/ps2/SDL_syssem.c
new file mode 100644
index 0000000..ccd09c0
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/thread/ps2/SDL_syssem.c
@@ -0,0 +1,122 @@
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_PS2
24
25// Semaphore functions for the PS2.
26
27#include <stdio.h>
28#include <stdlib.h>
29#include <kernel_util.h>
30
31#include <kernel.h>
32
33struct SDL_Semaphore
34{
35 s32 semid;
36};
37
38// Create a semaphore
39SDL_Semaphore *SDL_CreateSemaphore(Uint32 initial_value)
40{
41 SDL_Semaphore *sem;
42 ee_sema_t sema;
43
44 sem = (SDL_Semaphore *)SDL_malloc(sizeof(*sem));
45 if (sem) {
46 // TODO: Figure out the limit on the maximum value.
47 sema.init_count = initial_value;
48 sema.max_count = 255;
49 sema.option = 0;
50 sem->semid = CreateSema(&sema);
51
52 if (sem->semid < 0) {
53 SDL_SetError("Couldn't create semaphore");
54 SDL_free(sem);
55 sem = NULL;
56 }
57 }
58
59 return sem;
60}
61
62// Free the semaphore
63void SDL_DestroySemaphore(SDL_Semaphore *sem)
64{
65 if (sem) {
66 if (sem->semid > 0) {
67 DeleteSema(sem->semid);
68 sem->semid = 0;
69 }
70
71 SDL_free(sem);
72 }
73}
74
75bool SDL_WaitSemaphoreTimeoutNS(SDL_Semaphore *sem, Sint64 timeoutNS)
76{
77 u64 timeout_usec;
78 u64 *timeout_ptr;
79
80 if (!sem) {
81 return true;
82 }
83
84 if (timeoutNS == 0) {
85 return (PollSema(sem->semid) == 0);
86 }
87
88 timeout_ptr = NULL;
89
90 if (timeoutNS != -1) { // -1 == wait indefinitely.
91 timeout_usec = SDL_NS_TO_US(timeoutNS);
92 timeout_ptr = &timeout_usec;
93 }
94
95 return (WaitSemaEx(sem->semid, 1, timeout_ptr) == 0);
96}
97
98// Returns the current count of the semaphore
99Uint32 SDL_GetSemaphoreValue(SDL_Semaphore *sem)
100{
101 ee_sema_t info;
102
103 if (!sem) {
104 return 0;
105 }
106
107 if (ReferSemaStatus(sem->semid, &info) == 0) {
108 return info.count;
109 }
110 return 0;
111}
112
113void SDL_SignalSemaphore(SDL_Semaphore *sem)
114{
115 if (!sem) {
116 return;
117 }
118
119 SignalSema(sem->semid);
120}
121
122#endif // SDL_THREAD_PS2
diff --git a/contrib/SDL-3.2.8/src/thread/ps2/SDL_systhread.c b/contrib/SDL-3.2.8/src/thread/ps2/SDL_systhread.c
new file mode 100644
index 0000000..a13da42
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/thread/ps2/SDL_systhread.c
@@ -0,0 +1,143 @@
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_PS2
24
25// PS2 thread management routines for SDL
26
27#include <stdio.h>
28#include <stdlib.h>
29
30#include "../SDL_systhread.h"
31#include "../SDL_thread_c.h"
32#include <kernel.h>
33
34static void FinishThread(SDL_Thread *thread)
35{
36 ee_thread_status_t info;
37 int res;
38
39 res = ReferThreadStatus(thread->handle, &info);
40 TerminateThread(thread->handle);
41 DeleteThread(thread->handle);
42 DeleteSema((int)thread->endfunc);
43
44 if (res > 0) {
45 SDL_free(info.stack);
46 }
47}
48
49static int childThread(void *arg)
50{
51 SDL_Thread *thread = (SDL_Thread *)arg;
52 int res = thread->userfunc(thread->userdata);
53 SignalSema((int)thread->endfunc);
54 return res;
55}
56
57bool SDL_SYS_CreateThread(SDL_Thread *thread,
58 SDL_FunctionPointer pfnBeginThread,
59 SDL_FunctionPointer pfnEndThread)
60{
61 ee_thread_status_t status;
62 ee_thread_t eethread;
63 ee_sema_t sema;
64 size_t stack_size;
65 int priority = 32;
66
67 // Set priority of new thread to the same as the current thread
68 // status.size = sizeof(ee_thread_t);
69 if (ReferThreadStatus(GetThreadId(), &status) == 0) {
70 priority = status.current_priority;
71 }
72
73 stack_size = thread->stacksize ? ((int)thread->stacksize) : 0x1800;
74
75 // Create EE Thread
76 eethread.attr = 0;
77 eethread.option = 0;
78 eethread.func = &childThread;
79 eethread.stack = SDL_malloc(stack_size);
80 eethread.stack_size = stack_size;
81 eethread.gp_reg = &_gp;
82 eethread.initial_priority = priority;
83 thread->handle = CreateThread(&eethread);
84
85 if (thread->handle < 0) {
86 return SDL_SetError("CreateThread() failed");
87 }
88
89 // Prepare el semaphore for the ending function
90 sema.init_count = 0;
91 sema.max_count = 1;
92 sema.option = 0;
93 thread->endfunc = (void *)CreateSema(&sema);
94
95 if (StartThread(thread->handle, thread) < 0) {
96 return SDL_SetError("StartThread() failed");
97 }
98 return true;
99}
100
101void SDL_SYS_SetupThread(const char *name)
102{
103 // Do nothing.
104}
105
106SDL_ThreadID SDL_GetCurrentThreadID(void)
107{
108 return (SDL_ThreadID)GetThreadId();
109}
110
111void SDL_SYS_WaitThread(SDL_Thread *thread)
112{
113 WaitSema((int)thread->endfunc);
114 ReleaseWaitThread(thread->handle);
115 FinishThread(thread);
116}
117
118void SDL_SYS_DetachThread(SDL_Thread *thread)
119{
120 // Do nothing.
121}
122
123bool SDL_SYS_SetThreadPriority(SDL_ThreadPriority priority)
124{
125 int value;
126
127 if (priority == SDL_THREAD_PRIORITY_LOW) {
128 value = 111;
129 } else if (priority == SDL_THREAD_PRIORITY_HIGH) {
130 value = 32;
131 } else if (priority == SDL_THREAD_PRIORITY_TIME_CRITICAL) {
132 value = 16;
133 } else {
134 value = 50;
135 }
136
137 if (ChangeThreadPriority(GetThreadId(), value) < 0) {
138 return SDL_SetError("ChangeThreadPriority() failed");
139 }
140 return true;
141}
142
143#endif // SDL_THREAD_PS2
diff --git a/contrib/SDL-3.2.8/src/thread/ps2/SDL_systhread_c.h b/contrib/SDL-3.2.8/src/thread/ps2/SDL_systhread_c.h
new file mode 100644
index 0000000..2a811ce
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/thread/ps2/SDL_systhread_c.h
@@ -0,0 +1,22 @@
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
22typedef int32_t SYS_ThreadHandle;
diff --git a/contrib/SDL-3.2.8/src/thread/psp/SDL_sysmutex.c b/contrib/SDL-3.2.8/src/thread/psp/SDL_sysmutex.c
new file mode 100644
index 0000000..c8ed80d
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/thread/psp/SDL_sysmutex.c
@@ -0,0 +1,100 @@
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_PSP
24
25// An implementation of mutexes using semaphores
26
27#include "SDL_systhread_c.h"
28
29#include <pspthreadman.h>
30#include <pspkerror.h>
31
32#define SCE_KERNEL_MUTEX_ATTR_RECURSIVE 0x0200U
33
34struct SDL_Mutex
35{
36 SceLwMutexWorkarea lock;
37};
38
39SDL_Mutex *SDL_CreateMutex(void)
40{
41 SDL_Mutex *mutex = (SDL_Mutex *)SDL_malloc(sizeof(*mutex));
42 if (mutex) {
43 const SceInt32 res = sceKernelCreateLwMutex(
44 &mutex->lock,
45 "SDL mutex",
46 SCE_KERNEL_MUTEX_ATTR_RECURSIVE,
47 0,
48 NULL);
49
50 if (res < 0) {
51 SDL_free(mutex);
52 mutex = NULL;
53 SDL_SetError("Error trying to create mutex: %lx", res);
54 }
55 }
56 return mutex;
57}
58
59void SDL_DestroyMutex(SDL_Mutex *mutex)
60{
61 if (mutex) {
62 sceKernelDeleteLwMutex(&mutex->lock);
63 SDL_free(mutex);
64 }
65}
66
67void SDL_LockMutex(SDL_Mutex *mutex) SDL_NO_THREAD_SAFETY_ANALYSIS // clang doesn't know about NULL mutexes
68{
69 if (mutex) {
70 const SceInt32 res = sceKernelLockLwMutex(&mutex->lock, 1, NULL);
71 SDL_assert(res == SCE_KERNEL_ERROR_OK); // assume we're in a lot of trouble if this assert fails.
72 }
73}
74
75bool SDL_TryLockMutex(SDL_Mutex *mutex)
76{
77 bool result = true;
78 if (mutex) {
79 const SceInt32 res = sceKernelTryLockLwMutex(&mutex->lock, 1);
80 if (res == SCE_KERNEL_ERROR_OK) {
81 result = true;
82 } else if (res == SCE_KERNEL_ERROR_WAIT_TIMEOUT) {
83 result = false;
84 } else {
85 SDL_assert(res == SCE_KERNEL_ERROR_OK); // assume we're in a lot of trouble if this assert fails.
86 result = false;
87 }
88 }
89 return result;
90}
91
92void SDL_UnlockMutex(SDL_Mutex *mutex) SDL_NO_THREAD_SAFETY_ANALYSIS // clang doesn't know about NULL mutexes
93{
94 if (mutex) {
95 const SceInt32 res = sceKernelUnlockLwMutex(&mutex->lock, 1);
96 SDL_assert(res == SCE_KERNEL_ERROR_OK); // assume we're in a lot of trouble if this assert fails.
97 }
98}
99
100#endif // SDL_THREAD_PSP
diff --git a/contrib/SDL-3.2.8/src/thread/psp/SDL_sysmutex_c.h b/contrib/SDL-3.2.8/src/thread/psp/SDL_sysmutex_c.h
new file mode 100644
index 0000000..4b0c6f8
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/thread/psp/SDL_sysmutex_c.h
@@ -0,0 +1,21 @@
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"
diff --git a/contrib/SDL-3.2.8/src/thread/psp/SDL_syssem.c b/contrib/SDL-3.2.8/src/thread/psp/SDL_syssem.c
new file mode 100644
index 0000000..27e332f
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/thread/psp/SDL_syssem.c
@@ -0,0 +1,119 @@
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_PSP
24
25// Semaphore functions for the PSP.
26
27#include <stdio.h>
28#include <stdlib.h>
29
30#include <pspthreadman.h>
31#include <pspkerror.h>
32
33struct SDL_Semaphore
34{
35 SceUID semid;
36};
37
38// Create a semaphore
39SDL_Semaphore *SDL_CreateSemaphore(Uint32 initial_value)
40{
41 SDL_Semaphore *sem;
42
43 sem = (SDL_Semaphore *)SDL_malloc(sizeof(*sem));
44 if (sem) {
45 // TODO: Figure out the limit on the maximum value.
46 sem->semid = sceKernelCreateSema("SDL sema", 0, initial_value, 255, NULL);
47 if (sem->semid < 0) {
48 SDL_SetError("Couldn't create semaphore");
49 SDL_free(sem);
50 sem = NULL;
51 }
52 }
53
54 return sem;
55}
56
57// Free the semaphore
58void SDL_DestroySemaphore(SDL_Semaphore *sem)
59{
60 if (sem) {
61 if (sem->semid > 0) {
62 sceKernelDeleteSema(sem->semid);
63 sem->semid = 0;
64 }
65
66 SDL_free(sem);
67 }
68}
69
70/* TODO: This routine is a bit overloaded.
71 * If the timeout is 0 then just poll the semaphore; if it's -1, pass
72 * NULL to sceKernelWaitSema() so that it waits indefinitely; and if the timeout
73 * is specified, convert it to microseconds. */
74bool SDL_WaitSemaphoreTimeoutNS(SDL_Semaphore *sem, Sint64 timeoutNS)
75{
76 SceUInt timeoutUS;
77 SceUInt *pTimeout = NULL;
78
79 if (!sem) {
80 return true;
81 }
82
83 if (timeoutNS == 0) {
84 return (sceKernelPollSema(sem->semid, 1) == 0);
85 }
86
87 if (timeoutNS > 0) {
88 timeoutUS = (SceUInt)SDL_NS_TO_US(timeoutNS); // Convert to microseconds.
89 pTimeout = &timeoutUS;
90 }
91
92 return (sceKernelWaitSema(sem->semid, 1, pTimeout) == 0);
93}
94
95// Returns the current count of the semaphore
96Uint32 SDL_GetSemaphoreValue(SDL_Semaphore *sem)
97{
98 SceKernelSemaInfo info;
99
100 if (!sem) {
101 return 0;
102 }
103
104 if (sceKernelReferSemaStatus(sem->semid, &info) == 0) {
105 return info.currentCount;
106 }
107 return 0;
108}
109
110void SDL_SignalSemaphore(SDL_Semaphore *sem)
111{
112 if (!sem) {
113 return;
114 }
115
116 sceKernelSignalSema(sem->semid, 1);
117}
118
119#endif // SDL_THREAD_PSP
diff --git a/contrib/SDL-3.2.8/src/thread/psp/SDL_systhread.c b/contrib/SDL-3.2.8/src/thread/psp/SDL_systhread.c
new file mode 100644
index 0000000..3d60718
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/thread/psp/SDL_systhread.c
@@ -0,0 +1,112 @@
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_PSP
24
25// PSP thread management routines for SDL
26
27#include <stdio.h>
28#include <stdlib.h>
29
30#include "../SDL_systhread.h"
31#include "../SDL_thread_c.h"
32#include <pspkerneltypes.h>
33#include <pspthreadman.h>
34
35static int ThreadEntry(SceSize args, void *argp)
36{
37 SDL_RunThread(*(SDL_Thread **)argp);
38 return 0;
39}
40
41bool SDL_SYS_CreateThread(SDL_Thread *thread,
42 SDL_FunctionPointer pfnBeginThread,
43 SDL_FunctionPointer pfnEndThread)
44{
45 SceKernelThreadInfo status;
46 int priority = 32;
47
48 // Set priority of new thread to the same as the current thread
49 status.size = sizeof(SceKernelThreadInfo);
50 if (sceKernelReferThreadStatus(sceKernelGetThreadId(), &status) == 0) {
51 priority = status.currentPriority;
52 }
53
54 thread->handle = sceKernelCreateThread(thread->name, ThreadEntry,
55 priority, thread->stacksize ? ((int)thread->stacksize) : 0x8000,
56 PSP_THREAD_ATTR_VFPU, NULL);
57 if (thread->handle < 0) {
58 return SDL_SetError("sceKernelCreateThread() failed");
59 }
60
61 sceKernelStartThread(thread->handle, 4, &thread);
62 return true;
63}
64
65void SDL_SYS_SetupThread(const char *name)
66{
67 // Do nothing.
68}
69
70SDL_ThreadID SDL_GetCurrentThreadID(void)
71{
72 return (SDL_ThreadID)sceKernelGetThreadId();
73}
74
75void SDL_SYS_WaitThread(SDL_Thread *thread)
76{
77 sceKernelWaitThreadEnd(thread->handle, NULL);
78 sceKernelDeleteThread(thread->handle);
79}
80
81void SDL_SYS_DetachThread(SDL_Thread *thread)
82{
83 // !!! FIXME: is this correct?
84 sceKernelDeleteThread(thread->handle);
85}
86
87void SDL_SYS_KillThread(SDL_Thread *thread)
88{
89 sceKernelTerminateDeleteThread(thread->handle);
90}
91
92bool SDL_SYS_SetThreadPriority(SDL_ThreadPriority priority)
93{
94 int value;
95
96 if (priority == SDL_THREAD_PRIORITY_LOW) {
97 value = 111;
98 } else if (priority == SDL_THREAD_PRIORITY_HIGH) {
99 value = 32;
100 } else if (priority == SDL_THREAD_PRIORITY_TIME_CRITICAL) {
101 value = 16;
102 } else {
103 value = 50;
104 }
105
106 if (sceKernelChangeThreadPriority(sceKernelGetThreadId(), value) < 0) {
107 return SDL_SetError("sceKernelChangeThreadPriority() failed");
108 }
109 return true;
110}
111
112#endif // SDL_THREAD_PSP
diff --git a/contrib/SDL-3.2.8/src/thread/psp/SDL_systhread_c.h b/contrib/SDL-3.2.8/src/thread/psp/SDL_systhread_c.h
new file mode 100644
index 0000000..9e951ec
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/thread/psp/SDL_systhread_c.h
@@ -0,0 +1,24 @@
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 <pspkerneltypes.h>
23
24typedef SceUID SYS_ThreadHandle;
diff --git a/contrib/SDL-3.2.8/src/thread/pthread/SDL_syscond.c b/contrib/SDL-3.2.8/src/thread/pthread/SDL_syscond.c
new file mode 100644
index 0000000..c3e4098
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/thread/pthread/SDL_syscond.c
@@ -0,0 +1,128 @@
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 <sys/time.h>
24#include <time.h>
25#include <unistd.h>
26#include <errno.h>
27#include <pthread.h>
28
29#include "SDL_sysmutex_c.h"
30
31struct SDL_Condition
32{
33 pthread_cond_t cond;
34};
35
36// Create a condition variable
37SDL_Condition *SDL_CreateCondition(void)
38{
39 SDL_Condition *cond;
40
41 cond = (SDL_Condition *)SDL_malloc(sizeof(SDL_Condition));
42 if (cond) {
43 if (pthread_cond_init(&cond->cond, NULL) != 0) {
44 SDL_SetError("pthread_cond_init() failed");
45 SDL_free(cond);
46 cond = NULL;
47 }
48 }
49 return cond;
50}
51
52// Destroy a condition variable
53void SDL_DestroyCondition(SDL_Condition *cond)
54{
55 if (cond) {
56 pthread_cond_destroy(&cond->cond);
57 SDL_free(cond);
58 }
59}
60
61// Restart one of the threads that are waiting on the condition variable
62void SDL_SignalCondition(SDL_Condition *cond)
63{
64 if (!cond) {
65 return;
66 }
67
68 pthread_cond_signal(&cond->cond);
69}
70
71// Restart all threads that are waiting on the condition variable
72void SDL_BroadcastCondition(SDL_Condition *cond)
73{
74 if (!cond) {
75 return;
76 }
77
78 pthread_cond_broadcast(&cond->cond);
79}
80
81bool SDL_WaitConditionTimeoutNS(SDL_Condition *cond, SDL_Mutex *mutex, Sint64 timeoutNS)
82{
83#ifndef HAVE_CLOCK_GETTIME
84 struct timeval delta;
85#endif
86 struct timespec abstime;
87
88 if (!cond || !mutex) {
89 return true;
90 }
91
92 if (timeoutNS < 0) {
93 return (pthread_cond_wait(&cond->cond, &mutex->id) == 0);
94 }
95
96#ifdef HAVE_CLOCK_GETTIME
97 clock_gettime(CLOCK_REALTIME, &abstime);
98
99 abstime.tv_sec += (timeoutNS / SDL_NS_PER_SECOND);
100 abstime.tv_nsec += (timeoutNS % SDL_NS_PER_SECOND);
101#else
102 gettimeofday(&delta, NULL);
103
104 abstime.tv_sec = delta.tv_sec + (timeoutNS / SDL_NS_PER_SECOND);
105 abstime.tv_nsec = SDL_US_TO_NS(delta.tv_usec) + (timeoutNS % SDL_NS_PER_SECOND);
106#endif
107 while (abstime.tv_nsec >= 1000000000) {
108 abstime.tv_sec += 1;
109 abstime.tv_nsec -= 1000000000;
110 }
111
112 bool result;
113 int rc;
114tryagain:
115 rc = pthread_cond_timedwait(&cond->cond, &mutex->id, &abstime);
116 switch (rc) {
117 case EINTR:
118 goto tryagain;
119 // break; -Wunreachable-code-break
120 case ETIMEDOUT:
121 result = false;
122 break;
123 default:
124 result = true;
125 break;
126 }
127 return result;
128}
diff --git a/contrib/SDL-3.2.8/src/thread/pthread/SDL_sysmutex.c b/contrib/SDL-3.2.8/src/thread/pthread/SDL_sysmutex.c
new file mode 100644
index 0000000..7bba286
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/thread/pthread/SDL_sysmutex.c
@@ -0,0 +1,154 @@
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 <errno.h>
24#include <pthread.h>
25
26#include "SDL_sysmutex_c.h"
27
28SDL_Mutex *SDL_CreateMutex(void)
29{
30 SDL_Mutex *mutex;
31 pthread_mutexattr_t attr;
32
33 // Allocate the structure
34 mutex = (SDL_Mutex *)SDL_calloc(1, sizeof(*mutex));
35 if (mutex) {
36 pthread_mutexattr_init(&attr);
37#ifdef SDL_THREAD_PTHREAD_RECURSIVE_MUTEX
38 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
39#elif defined(SDL_THREAD_PTHREAD_RECURSIVE_MUTEX_NP)
40 pthread_mutexattr_setkind_np(&attr, PTHREAD_MUTEX_RECURSIVE_NP);
41#else
42 // No extra attributes necessary
43#endif
44 if (pthread_mutex_init(&mutex->id, &attr) != 0) {
45 SDL_SetError("pthread_mutex_init() failed");
46 SDL_free(mutex);
47 mutex = NULL;
48 }
49 }
50 return mutex;
51}
52
53void SDL_DestroyMutex(SDL_Mutex *mutex)
54{
55 if (mutex) {
56 pthread_mutex_destroy(&mutex->id);
57 SDL_free(mutex);
58 }
59}
60
61void SDL_LockMutex(SDL_Mutex *mutex) SDL_NO_THREAD_SAFETY_ANALYSIS // clang doesn't know about NULL mutexes
62{
63 if (mutex) {
64#ifdef FAKE_RECURSIVE_MUTEX
65 pthread_t this_thread = pthread_self();
66 if (mutex->owner == this_thread) {
67 ++mutex->recursive;
68 } else {
69 /* The order of operations is important.
70 We set the locking thread id after we obtain the lock
71 so unlocks from other threads will fail.
72 */
73 const int rc = pthread_mutex_lock(&mutex->id);
74 SDL_assert(rc == 0); // assume we're in a lot of trouble if this assert fails.
75 mutex->owner = this_thread;
76 mutex->recursive = 0;
77 }
78#else
79 const int rc = pthread_mutex_lock(&mutex->id);
80 SDL_assert(rc == 0); // assume we're in a lot of trouble if this assert fails.
81#endif
82 }
83}
84
85bool SDL_TryLockMutex(SDL_Mutex *mutex)
86{
87 bool result = true;
88
89 if (mutex) {
90#ifdef FAKE_RECURSIVE_MUTEX
91 pthread_t this_thread = pthread_self();
92 if (mutex->owner == this_thread) {
93 ++mutex->recursive;
94 } else {
95 /* The order of operations is important.
96 We set the locking thread id after we obtain the lock
97 so unlocks from other threads will fail.
98 */
99 const int rc = pthread_mutex_trylock(&mutex->id);
100 if (rc == 0) {
101 mutex->owner = this_thread;
102 mutex->recursive = 0;
103 } else if (rc == EBUSY) {
104 result = false;
105 } else {
106 SDL_assert(!"Error trying to lock mutex"); // assume we're in a lot of trouble if this assert fails.
107 result = false;
108 }
109 }
110#else
111 const int rc = pthread_mutex_trylock(&mutex->id);
112 if (rc != 0) {
113 if (rc == EBUSY) {
114 result = false;
115 } else {
116 SDL_assert(!"Error trying to lock mutex"); // assume we're in a lot of trouble if this assert fails.
117 result = false;
118 }
119 }
120#endif
121 }
122
123 return result;
124}
125
126void SDL_UnlockMutex(SDL_Mutex *mutex) SDL_NO_THREAD_SAFETY_ANALYSIS // clang doesn't know about NULL mutexes
127{
128 if (mutex) {
129#ifdef FAKE_RECURSIVE_MUTEX
130 // We can only unlock the mutex if we own it
131 if (pthread_self() == mutex->owner) {
132 if (mutex->recursive) {
133 --mutex->recursive;
134 } else {
135 /* The order of operations is important.
136 First reset the owner so another thread doesn't lock
137 the mutex and set the ownership before we reset it,
138 then release the lock semaphore.
139 */
140 mutex->owner = 0;
141 pthread_mutex_unlock(&mutex->id);
142 }
143 } else {
144 SDL_SetError("mutex not owned by this thread");
145 return;
146 }
147
148#else
149 const int rc = pthread_mutex_unlock(&mutex->id);
150 SDL_assert(rc == 0); // assume we're in a lot of trouble if this assert fails.
151#endif // FAKE_RECURSIVE_MUTEX
152 }
153}
154
diff --git a/contrib/SDL-3.2.8/src/thread/pthread/SDL_sysmutex_c.h b/contrib/SDL-3.2.8/src/thread/pthread/SDL_sysmutex_c.h
new file mode 100644
index 0000000..f69e7b3
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/thread/pthread/SDL_sysmutex_c.h
@@ -0,0 +1,40 @@
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_mutex_c_h_
24#define SDL_mutex_c_h_
25
26#if !(defined(SDL_THREAD_PTHREAD_RECURSIVE_MUTEX) || \
27 defined(SDL_THREAD_PTHREAD_RECURSIVE_MUTEX_NP))
28#define FAKE_RECURSIVE_MUTEX
29#endif
30
31struct SDL_Mutex
32{
33 pthread_mutex_t id;
34#ifdef FAKE_RECURSIVE_MUTEX
35 int recursive;
36 pthread_t owner;
37#endif
38};
39
40#endif // SDL_mutex_c_h_
diff --git a/contrib/SDL-3.2.8/src/thread/pthread/SDL_sysrwlock.c b/contrib/SDL-3.2.8/src/thread/pthread/SDL_sysrwlock.c
new file mode 100644
index 0000000..3cf02e5
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/thread/pthread/SDL_sysrwlock.c
@@ -0,0 +1,113 @@
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 <errno.h>
24#include <pthread.h>
25
26struct SDL_RWLock
27{
28 pthread_rwlock_t id;
29};
30
31
32SDL_RWLock *SDL_CreateRWLock(void)
33{
34 SDL_RWLock *rwlock;
35
36 // Allocate the structure
37 rwlock = (SDL_RWLock *)SDL_calloc(1, sizeof(*rwlock));
38 if (rwlock) {
39 if (pthread_rwlock_init(&rwlock->id, NULL) != 0) {
40 SDL_SetError("pthread_rwlock_init() failed");
41 SDL_free(rwlock);
42 rwlock = NULL;
43 }
44 }
45 return rwlock;
46}
47
48void SDL_DestroyRWLock(SDL_RWLock *rwlock)
49{
50 if (rwlock) {
51 pthread_rwlock_destroy(&rwlock->id);
52 SDL_free(rwlock);
53 }
54}
55
56void SDL_LockRWLockForReading(SDL_RWLock *rwlock) SDL_NO_THREAD_SAFETY_ANALYSIS // clang doesn't know about NULL mutexes
57{
58 if (rwlock) {
59 const int rc = pthread_rwlock_rdlock(&rwlock->id);
60 SDL_assert(rc == 0); // assume we're in a lot of trouble if this assert fails.
61 }
62}
63
64void SDL_LockRWLockForWriting(SDL_RWLock *rwlock) SDL_NO_THREAD_SAFETY_ANALYSIS // clang doesn't know about NULL mutexes
65{
66 if (rwlock) {
67 const int rc = pthread_rwlock_wrlock(&rwlock->id);
68 SDL_assert(rc == 0); // assume we're in a lot of trouble if this assert fails.
69 }
70}
71
72bool SDL_TryLockRWLockForReading(SDL_RWLock *rwlock)
73{
74 bool result = true;
75
76 if (rwlock) {
77 const int rc = pthread_rwlock_tryrdlock(&rwlock->id);
78 if (rc != 0) {
79 result = false;
80 if (rc != EBUSY) {
81 SDL_assert(!"Error trying to lock rwlock for reading"); // assume we're in a lot of trouble if this assert fails.
82 }
83 }
84 }
85
86 return result;
87}
88
89bool SDL_TryLockRWLockForWriting(SDL_RWLock *rwlock)
90{
91 bool result = true;
92
93 if (rwlock) {
94 const int rc = pthread_rwlock_trywrlock(&rwlock->id);
95 if (rc != 0) {
96 result = false;
97 if (rc != EBUSY) {
98 SDL_assert(!"Error trying to lock rwlock for writing"); // assume we're in a lot of trouble if this assert fails.
99 }
100 }
101 }
102
103 return result;
104}
105
106void SDL_UnlockRWLock(SDL_RWLock *rwlock) SDL_NO_THREAD_SAFETY_ANALYSIS // clang doesn't know about NULL mutexes
107{
108 if (rwlock) {
109 const int rc = pthread_rwlock_unlock(&rwlock->id);
110 SDL_assert(rc == 0); // assume we're in a lot of trouble if this assert fails.
111 }
112}
113
diff --git a/contrib/SDL-3.2.8/src/thread/pthread/SDL_syssem.c b/contrib/SDL-3.2.8/src/thread/pthread/SDL_syssem.c
new file mode 100644
index 0000000..d5d3298
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/thread/pthread/SDL_syssem.c
@@ -0,0 +1,160 @@
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 <errno.h>
24#include <pthread.h>
25#include <semaphore.h>
26#include <sys/time.h>
27#include <time.h>
28
29// Wrapper around POSIX 1003.1b semaphores
30
31#if defined(SDL_PLATFORM_MACOS) || defined(SDL_PLATFORM_IOS)
32// macOS doesn't support sem_getvalue() as of version 10.4
33#include "../generic/SDL_syssem.c"
34#else
35
36struct SDL_Semaphore
37{
38 sem_t sem;
39};
40
41// Create a semaphore, initialized with value
42SDL_Semaphore *SDL_CreateSemaphore(Uint32 initial_value)
43{
44 SDL_Semaphore *sem = (SDL_Semaphore *)SDL_malloc(sizeof(SDL_Semaphore));
45 if (sem) {
46 if (sem_init(&sem->sem, 0, initial_value) < 0) {
47 SDL_SetError("sem_init() failed");
48 SDL_free(sem);
49 sem = NULL;
50 }
51 }
52 return sem;
53}
54
55void SDL_DestroySemaphore(SDL_Semaphore *sem)
56{
57 if (sem) {
58 sem_destroy(&sem->sem);
59 SDL_free(sem);
60 }
61}
62
63bool SDL_WaitSemaphoreTimeoutNS(SDL_Semaphore *sem, Sint64 timeoutNS)
64{
65#ifdef HAVE_SEM_TIMEDWAIT
66#ifndef HAVE_CLOCK_GETTIME
67 struct timeval now;
68#endif
69 struct timespec ts_timeout;
70#else
71 Uint64 stop_time;
72#endif
73
74 if (!sem) {
75 return true;
76 }
77
78 // Try the easy cases first
79 if (timeoutNS == 0) {
80 return (sem_trywait(&sem->sem) == 0);
81 }
82
83 if (timeoutNS < 0) {
84 int rc;
85 do {
86 rc = sem_wait(&sem->sem);
87 } while (rc < 0 && errno == EINTR);
88
89 return (rc == 0);
90 }
91
92#ifdef HAVE_SEM_TIMEDWAIT
93 /* Setup the timeout. sem_timedwait doesn't wait for
94 * a lapse of time, but until we reach a certain time.
95 * This time is now plus the timeout.
96 */
97#ifdef HAVE_CLOCK_GETTIME
98 clock_gettime(CLOCK_REALTIME, &ts_timeout);
99
100 // Add our timeout to current time
101 ts_timeout.tv_sec += (timeoutNS / SDL_NS_PER_SECOND);
102 ts_timeout.tv_nsec += (timeoutNS % SDL_NS_PER_SECOND);
103#else
104 gettimeofday(&now, NULL);
105
106 // Add our timeout to current time
107 ts_timeout.tv_sec = now.tv_sec + (timeoutNS / SDL_NS_PER_SECOND);
108 ts_timeout.tv_nsec = SDL_US_TO_NS(now.tv_usec) + (timeoutNS % SDL_NS_PER_SECOND);
109#endif
110
111 // Wrap the second if needed
112 while (ts_timeout.tv_nsec >= 1000000000) {
113 ts_timeout.tv_sec += 1;
114 ts_timeout.tv_nsec -= 1000000000;
115 }
116
117 // Wait.
118 int rc;
119 do {
120 rc = sem_timedwait(&sem->sem, &ts_timeout);
121 } while (rc < 0 && errno == EINTR);
122
123 return (rc == 0);
124#else
125 stop_time = SDL_GetTicksNS() + timeoutNS;
126 while (sem_trywait(&sem->sem) != 0) {
127 if (SDL_GetTicksNS() >= stop_time) {
128 return false;
129 }
130 SDL_DelayNS(100);
131 }
132 return true;
133#endif // HAVE_SEM_TIMEDWAIT
134}
135
136Uint32 SDL_GetSemaphoreValue(SDL_Semaphore *sem)
137{
138 int ret = 0;
139
140 if (!sem) {
141 return 0;
142 }
143
144 sem_getvalue(&sem->sem, &ret);
145 if (ret < 0) {
146 ret = 0;
147 }
148 return (Uint32)ret;
149}
150
151void SDL_SignalSemaphore(SDL_Semaphore *sem)
152{
153 if (!sem) {
154 return;
155 }
156
157 sem_post(&sem->sem);
158}
159
160#endif // SDL_PLATFORM_MACOS
diff --git a/contrib/SDL-3.2.8/src/thread/pthread/SDL_systhread.c b/contrib/SDL-3.2.8/src/thread/pthread/SDL_systhread.c
new file mode 100644
index 0000000..ae4a94c
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/thread/pthread/SDL_systhread.c
@@ -0,0 +1,293 @@
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 <pthread.h>
24
25#ifdef HAVE_PTHREAD_NP_H
26#include <pthread_np.h>
27#endif
28
29#ifdef HAVE_SIGNAL_H
30#include <signal.h>
31#endif
32#include <errno.h>
33
34#ifdef SDL_PLATFORM_LINUX
35#include <sys/time.h>
36#include <sys/resource.h>
37#include <sys/syscall.h>
38#include <unistd.h>
39
40#include "../../core/linux/SDL_dbus.h"
41#endif // SDL_PLATFORM_LINUX
42
43#if (defined(SDL_PLATFORM_LINUX) || defined(SDL_PLATFORM_ANDROID) || defined(SDL_PLATFORM_MACOS) || defined(SDL_PLATFORM_IOS)) && defined(HAVE_DLOPEN)
44#include <dlfcn.h>
45#ifndef RTLD_DEFAULT
46#define RTLD_DEFAULT NULL
47#endif
48#endif
49
50#include "../SDL_thread_c.h"
51#include "../SDL_systhread.h"
52#ifdef SDL_PLATFORM_ANDROID
53#include "../../core/android/SDL_android.h"
54#endif
55
56#ifdef SDL_PLATFORM_HAIKU
57#include <kernel/OS.h>
58#endif
59
60#ifdef HAVE_SIGNAL_H
61// List of signals to mask in the subthreads
62static const int sig_list[] = {
63 SIGHUP, SIGINT, SIGQUIT, SIGPIPE, SIGALRM, SIGTERM, SIGCHLD, SIGWINCH,
64 SIGVTALRM, SIGPROF, 0
65};
66#endif
67
68static void *RunThread(void *data)
69{
70#ifdef SDL_PLATFORM_ANDROID
71 Android_JNI_SetupThread();
72#endif
73 SDL_RunThread((SDL_Thread *)data);
74 return NULL;
75}
76
77#if (defined(SDL_PLATFORM_MACOS) || defined(SDL_PLATFORM_IOS)) && defined(HAVE_DLOPEN)
78static bool checked_setname = false;
79static int (*ppthread_setname_np)(const char *) = NULL;
80#elif (defined(SDL_PLATFORM_LINUX) || defined(SDL_PLATFORM_ANDROID)) && defined(HAVE_DLOPEN)
81static bool checked_setname = false;
82static int (*ppthread_setname_np)(pthread_t, const char *) = NULL;
83#endif
84bool SDL_SYS_CreateThread(SDL_Thread *thread,
85 SDL_FunctionPointer pfnBeginThread,
86 SDL_FunctionPointer pfnEndThread)
87{
88 pthread_attr_t type;
89
90// do this here before any threads exist, so there's no race condition.
91#if (defined(SDL_PLATFORM_MACOS) || defined(SDL_PLATFORM_IOS) || defined(SDL_PLATFORM_LINUX) || defined(SDL_PLATFORM_ANDROID)) && defined(HAVE_DLOPEN)
92 if (!checked_setname) {
93 void *fn = dlsym(RTLD_DEFAULT, "pthread_setname_np");
94#if defined(SDL_PLATFORM_MACOS) || defined(SDL_PLATFORM_IOS)
95 ppthread_setname_np = (int (*)(const char *))fn;
96#elif defined(SDL_PLATFORM_LINUX) || defined(SDL_PLATFORM_ANDROID)
97 ppthread_setname_np = (int (*)(pthread_t, const char *))fn;
98#endif
99 checked_setname = true;
100 }
101 #endif
102
103 // Set the thread attributes
104 if (pthread_attr_init(&type) != 0) {
105 return SDL_SetError("Couldn't initialize pthread attributes");
106 }
107 pthread_attr_setdetachstate(&type, PTHREAD_CREATE_JOINABLE);
108
109 // Set caller-requested stack size. Otherwise: use the system default.
110 if (thread->stacksize) {
111 pthread_attr_setstacksize(&type, thread->stacksize);
112 }
113
114 // Create the thread and go!
115 if (pthread_create(&thread->handle, &type, RunThread, thread) != 0) {
116 return SDL_SetError("Not enough resources to create thread");
117 }
118
119 return true;
120}
121
122void SDL_SYS_SetupThread(const char *name)
123{
124#ifdef HAVE_SIGNAL_H
125 int i;
126 sigset_t mask;
127#endif
128
129 if (name) {
130#if (defined(SDL_PLATFORM_MACOS) || defined(SDL_PLATFORM_IOS) || defined(SDL_PLATFORM_LINUX) || defined(SDL_PLATFORM_ANDROID)) && defined(HAVE_DLOPEN)
131 SDL_assert(checked_setname);
132 if (ppthread_setname_np) {
133#if defined(SDL_PLATFORM_MACOS) || defined(SDL_PLATFORM_IOS)
134 ppthread_setname_np(name);
135#elif defined(SDL_PLATFORM_LINUX) || defined(SDL_PLATFORM_ANDROID)
136 if (ppthread_setname_np(pthread_self(), name) == ERANGE) {
137 char namebuf[16]; // Limited to 16 char
138 SDL_strlcpy(namebuf, name, sizeof(namebuf));
139 ppthread_setname_np(pthread_self(), namebuf);
140 }
141#endif
142 }
143#elif defined(HAVE_PTHREAD_SETNAME_NP)
144#ifdef SDL_PLATFORM_NETBSD
145 pthread_setname_np(pthread_self(), "%s", name);
146#else
147 if (pthread_setname_np(pthread_self(), name) == ERANGE) {
148 char namebuf[16]; // Limited to 16 char
149 SDL_strlcpy(namebuf, name, sizeof(namebuf));
150 pthread_setname_np(pthread_self(), namebuf);
151 }
152#endif
153#elif defined(HAVE_PTHREAD_SET_NAME_NP)
154 pthread_set_name_np(pthread_self(), name);
155#elif defined(SDL_PLATFORM_HAIKU)
156 // The docs say the thread name can't be longer than B_OS_NAME_LENGTH.
157 char namebuf[B_OS_NAME_LENGTH];
158 SDL_strlcpy(namebuf, name, sizeof(namebuf));
159 rename_thread(find_thread(NULL), namebuf);
160#endif
161 }
162
163#ifdef HAVE_SIGNAL_H
164 // Mask asynchronous signals for this thread
165 sigemptyset(&mask);
166 for (i = 0; sig_list[i]; ++i) {
167 sigaddset(&mask, sig_list[i]);
168 }
169 pthread_sigmask(SIG_BLOCK, &mask, 0);
170#endif
171
172#ifdef PTHREAD_CANCEL_ASYNCHRONOUS
173 // Allow ourselves to be asynchronously cancelled
174 {
175 int oldstate;
176 pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldstate);
177 }
178#endif
179}
180
181SDL_ThreadID SDL_GetCurrentThreadID(void)
182{
183 return (SDL_ThreadID)pthread_self();
184}
185
186bool SDL_SYS_SetThreadPriority(SDL_ThreadPriority priority)
187{
188#ifdef SDL_PLATFORM_RISCOS
189 // FIXME: Setting thread priority does not seem to be supported
190 return true;
191#else
192 struct sched_param sched;
193 int policy;
194 int pri_policy;
195 pthread_t thread = pthread_self();
196 const char *policyhint = SDL_GetHint(SDL_HINT_THREAD_PRIORITY_POLICY);
197 const bool timecritical_realtime_hint = SDL_GetHintBoolean(SDL_HINT_THREAD_FORCE_REALTIME_TIME_CRITICAL, false);
198
199 if (pthread_getschedparam(thread, &policy, &sched) != 0) {
200 return SDL_SetError("pthread_getschedparam() failed");
201 }
202
203 /* Higher priority levels may require changing the pthread scheduler policy
204 * for the thread. SDL will make such changes by default but there is
205 * also a hint allowing that behavior to be overridden. */
206 switch (priority) {
207 case SDL_THREAD_PRIORITY_LOW:
208 case SDL_THREAD_PRIORITY_NORMAL:
209 pri_policy = SCHED_OTHER;
210 break;
211 case SDL_THREAD_PRIORITY_HIGH:
212 case SDL_THREAD_PRIORITY_TIME_CRITICAL:
213#if defined(SDL_PLATFORM_MACOS) || defined(SDL_PLATFORM_IOS) || defined(SDL_PLATFORM_TVOS)
214 // Apple requires SCHED_RR for high priority threads
215 pri_policy = SCHED_RR;
216 break;
217#else
218 pri_policy = SCHED_OTHER;
219 break;
220#endif
221 default:
222 pri_policy = policy;
223 break;
224 }
225
226 if (timecritical_realtime_hint && priority == SDL_THREAD_PRIORITY_TIME_CRITICAL) {
227 pri_policy = SCHED_RR;
228 }
229
230 if (policyhint) {
231 if (SDL_strcmp(policyhint, "current") == 0) {
232 // Leave current thread scheduler policy unchanged
233 } else if (SDL_strcmp(policyhint, "other") == 0) {
234 policy = SCHED_OTHER;
235 } else if (SDL_strcmp(policyhint, "rr") == 0) {
236 policy = SCHED_RR;
237 } else if (SDL_strcmp(policyhint, "fifo") == 0) {
238 policy = SCHED_FIFO;
239 } else {
240 policy = pri_policy;
241 }
242 } else {
243 policy = pri_policy;
244 }
245
246#ifdef SDL_PLATFORM_LINUX
247 {
248 pid_t linuxTid = syscall(SYS_gettid);
249 return SDL_SetLinuxThreadPriorityAndPolicy(linuxTid, priority, policy);
250 }
251#else
252 if (priority == SDL_THREAD_PRIORITY_LOW) {
253 sched.sched_priority = sched_get_priority_min(policy);
254 } else if (priority == SDL_THREAD_PRIORITY_TIME_CRITICAL) {
255 sched.sched_priority = sched_get_priority_max(policy);
256 } else {
257 int min_priority = sched_get_priority_min(policy);
258 int max_priority = sched_get_priority_max(policy);
259
260#if defined(SDL_PLATFORM_MACOS) || defined(SDL_PLATFORM_IOS) || defined(SDL_PLATFORM_TVOS)
261 if (min_priority == 15 && max_priority == 47) {
262 // Apple has a specific set of thread priorities
263 if (priority == SDL_THREAD_PRIORITY_HIGH) {
264 sched.sched_priority = 45;
265 } else {
266 sched.sched_priority = 37;
267 }
268 } else
269#endif // SDL_PLATFORM_MACOS || SDL_PLATFORM_IOS || SDL_PLATFORM_TVOS
270 {
271 sched.sched_priority = (min_priority + (max_priority - min_priority) / 2);
272 if (priority == SDL_THREAD_PRIORITY_HIGH) {
273 sched.sched_priority += ((max_priority - min_priority) / 4);
274 }
275 }
276 }
277 if (pthread_setschedparam(thread, policy, &sched) != 0) {
278 return SDL_SetError("pthread_setschedparam() failed");
279 }
280 return true;
281#endif // linux
282#endif // #if SDL_PLATFORM_RISCOS
283}
284
285void SDL_SYS_WaitThread(SDL_Thread *thread)
286{
287 pthread_join(thread->handle, 0);
288}
289
290void SDL_SYS_DetachThread(SDL_Thread *thread)
291{
292 pthread_detach(thread->handle);
293}
diff --git a/contrib/SDL-3.2.8/src/thread/pthread/SDL_systhread_c.h b/contrib/SDL-3.2.8/src/thread/pthread/SDL_systhread_c.h
new file mode 100644
index 0000000..bdfcd68
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/thread/pthread/SDL_systhread_c.h
@@ -0,0 +1,25 @@
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 <pthread.h>
24
25typedef pthread_t SYS_ThreadHandle;
diff --git a/contrib/SDL-3.2.8/src/thread/pthread/SDL_systls.c b/contrib/SDL-3.2.8/src/thread/pthread/SDL_systls.c
new file mode 100644
index 0000000..18e9a01
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/thread/pthread/SDL_systls.c
@@ -0,0 +1,78 @@
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#include "../SDL_systhread.h"
23#include "../SDL_thread_c.h"
24
25#include <pthread.h>
26
27#define INVALID_PTHREAD_KEY ((pthread_key_t)-1)
28
29static pthread_key_t thread_local_storage = INVALID_PTHREAD_KEY;
30static bool generic_local_storage = false;
31
32void SDL_SYS_InitTLSData(void)
33{
34 if (thread_local_storage == INVALID_PTHREAD_KEY && !generic_local_storage) {
35 if (pthread_key_create(&thread_local_storage, NULL) != 0) {
36 thread_local_storage = INVALID_PTHREAD_KEY;
37 SDL_Generic_InitTLSData();
38 generic_local_storage = true;
39 }
40 }
41}
42
43SDL_TLSData *SDL_SYS_GetTLSData(void)
44{
45 if (generic_local_storage) {
46 return SDL_Generic_GetTLSData();
47 }
48
49 if (thread_local_storage != INVALID_PTHREAD_KEY) {
50 return (SDL_TLSData *)pthread_getspecific(thread_local_storage);
51 }
52 return NULL;
53}
54
55bool SDL_SYS_SetTLSData(SDL_TLSData *data)
56{
57 if (generic_local_storage) {
58 return SDL_Generic_SetTLSData(data);
59 }
60
61 if (pthread_setspecific(thread_local_storage, data) != 0) {
62 return SDL_SetError("pthread_setspecific() failed");
63 }
64 return true;
65}
66
67void SDL_SYS_QuitTLSData(void)
68{
69 if (generic_local_storage) {
70 SDL_Generic_QuitTLSData();
71 generic_local_storage = false;
72 } else {
73 if (thread_local_storage != INVALID_PTHREAD_KEY) {
74 pthread_key_delete(thread_local_storage);
75 thread_local_storage = INVALID_PTHREAD_KEY;
76 }
77 }
78}
diff --git a/contrib/SDL-3.2.8/src/thread/vita/SDL_sysmutex.c b/contrib/SDL-3.2.8/src/thread/vita/SDL_sysmutex.c
new file mode 100644
index 0000000..8a3664c
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/thread/vita/SDL_sysmutex.c
@@ -0,0 +1,97 @@
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_VITA
24
25#include "SDL_systhread_c.h"
26
27#include <psp2/kernel/threadmgr.h>
28#include <psp2/kernel/error.h>
29
30struct SDL_Mutex
31{
32 SceKernelLwMutexWork lock;
33};
34
35SDL_Mutex *SDL_CreateMutex(void)
36{
37 SDL_Mutex *mutex = (SDL_Mutex *)SDL_malloc(sizeof(*mutex));
38 if (mutex) {
39 const SceInt32 res = sceKernelCreateLwMutex(
40 &mutex->lock,
41 "SDL mutex",
42 SCE_KERNEL_MUTEX_ATTR_RECURSIVE,
43 0,
44 NULL);
45
46 if (res < 0) {
47 SDL_free(mutex);
48 mutex = NULL;
49 SDL_SetError("Error trying to create mutex: %x", res);
50 }
51 }
52 return mutex;
53}
54
55void SDL_DestroyMutex(SDL_Mutex *mutex)
56{
57 if (mutex) {
58 sceKernelDeleteLwMutex(&mutex->lock);
59 SDL_free(mutex);
60 }
61}
62
63void SDL_LockMutex(SDL_Mutex *mutex) SDL_NO_THREAD_SAFETY_ANALYSIS // clang doesn't know about NULL mutexes
64{
65 if (mutex) {
66 const SceInt32 res = sceKernelLockLwMutex(&mutex->lock, 1, NULL);
67 SDL_assert(res == SCE_KERNEL_OK); // assume we're in a lot of trouble if this assert fails.
68 }
69}
70
71bool SDL_TryLockMutex(SDL_Mutex *mutex)
72{
73 bool result = true;
74
75 if (mutex) {
76 const SceInt32 res = sceKernelTryLockLwMutex(&mutex->lock, 1);
77 if (res == SCE_KERNEL_OK) {
78 result = true;
79 } else if (res == SCE_KERNEL_ERROR_MUTEX_FAILED_TO_OWN) {
80 result = false;
81 } else {
82 SDL_assert(res == SCE_KERNEL_OK); // assume we're in a lot of trouble if this assert fails.
83 result = false;
84 }
85 }
86 return result;
87}
88
89void SDL_UnlockMutex(SDL_Mutex *mutex) SDL_NO_THREAD_SAFETY_ANALYSIS // clang doesn't know about NULL mutexes
90{
91 if (mutex) {
92 const SceInt32 res = sceKernelUnlockLwMutex(&mutex->lock, 1);
93 SDL_assert(res == SCE_KERNEL_OK); // assume we're in a lot of trouble if this assert fails.
94 }
95}
96
97#endif // SDL_THREAD_VITA
diff --git a/contrib/SDL-3.2.8/src/thread/vita/SDL_sysmutex_c.h b/contrib/SDL-3.2.8/src/thread/vita/SDL_sysmutex_c.h
new file mode 100644
index 0000000..4b0c6f8
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/thread/vita/SDL_sysmutex_c.h
@@ -0,0 +1,21 @@
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"
diff --git a/contrib/SDL-3.2.8/src/thread/vita/SDL_syssem.c b/contrib/SDL-3.2.8/src/thread/vita/SDL_syssem.c
new file mode 100644
index 0000000..3c6d28a
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/thread/vita/SDL_syssem.c
@@ -0,0 +1,122 @@
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_VITA
24
25// Semaphore functions for the VITA.
26
27#include <stdio.h>
28#include <stdlib.h>
29
30#include <psp2/types.h>
31#include <psp2/kernel/error.h>
32#include <psp2/kernel/threadmgr.h>
33
34struct SDL_Semaphore
35{
36 SceUID semid;
37};
38
39// Create a semaphore
40SDL_Semaphore *SDL_CreateSemaphore(Uint32 initial_value)
41{
42 SDL_Semaphore *sem;
43
44 sem = (SDL_Semaphore *)SDL_malloc(sizeof(*sem));
45 if (sem) {
46 // TODO: Figure out the limit on the maximum value.
47 sem->semid = sceKernelCreateSema("SDL sema", 0, initial_value, 255, NULL);
48 if (sem->semid < 0) {
49 SDL_SetError("Couldn't create semaphore");
50 SDL_free(sem);
51 sem = NULL;
52 }
53 }
54
55 return sem;
56}
57
58// Free the semaphore
59void SDL_DestroySemaphore(SDL_Semaphore *sem)
60{
61 if (sem) {
62 if (sem->semid > 0) {
63 sceKernelDeleteSema(sem->semid);
64 sem->semid = 0;
65 }
66
67 SDL_free(sem);
68 }
69}
70
71/* TODO: This routine is a bit overloaded.
72 * If the timeout is 0 then just poll the semaphore; if it's -1, pass
73 * NULL to sceKernelWaitSema() so that it waits indefinitely; and if the timeout
74 * is specified, convert it to microseconds. */
75bool SDL_WaitSemaphoreTimeoutNS(SDL_Semaphore *sem, Sint64 timeoutNS)
76{
77 SceUInt timeoutUS;
78 SceUInt *pTimeout = NULL;
79
80 if (!sem) {
81 return true;
82 }
83
84 if (timeoutNS == 0) {
85 return (sceKernelPollSema(sem->semid, 1) == 0);
86 }
87
88 if (timeoutNS > 0) {
89 timeoutUS = (SceUInt)SDL_NS_TO_US(timeoutNS); // Convert to microseconds.
90 pTimeout = &timeoutUS;
91 }
92
93 return (sceKernelWaitSema(sem->semid, 1, pTimeout) == 0);
94}
95
96// Returns the current count of the semaphore
97Uint32 SDL_GetSemaphoreValue(SDL_Semaphore *sem)
98{
99 SceKernelSemaInfo info;
100 info.size = sizeof(info);
101
102 if (!sem) {
103 return 0;
104 }
105
106 if (sceKernelGetSemaInfo(sem->semid, &info) >= 0) {
107 return info.currentCount;
108 }
109
110 return 0;
111}
112
113void SDL_SignalSemaphore(SDL_Semaphore *sem)
114{
115 if (!sem) {
116 return;
117 }
118
119 sceKernelSignalSema(sem->semid, 1);
120}
121
122#endif // SDL_THREAD_VITA
diff --git a/contrib/SDL-3.2.8/src/thread/vita/SDL_systhread.c b/contrib/SDL-3.2.8/src/thread/vita/SDL_systhread.c
new file mode 100644
index 0000000..344d911
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/thread/vita/SDL_systhread.c
@@ -0,0 +1,139 @@
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_VITA
24
25// VITA thread management routines for SDL
26
27#include <stdio.h>
28#include <stdlib.h>
29
30#include "../SDL_systhread.h"
31#include "../SDL_thread_c.h"
32#include <psp2/types.h>
33#include <psp2/kernel/threadmgr.h>
34
35#define VITA_THREAD_STACK_SIZE_MIN 0x1000 // 4KiB
36#define VITA_THREAD_STACK_SIZE_MAX 0x2000000 // 32MiB
37#define VITA_THREAD_STACK_SIZE_DEFAULT 0x10000 // 64KiB
38#define VITA_THREAD_NAME_MAX 32
39
40#define VITA_THREAD_PRIORITY_LOW 191
41#define VITA_THREAD_PRIORITY_NORMAL 160
42#define VITA_THREAD_PRIORITY_HIGH 112
43#define VITA_THREAD_PRIORITY_TIME_CRITICAL 64
44
45static int ThreadEntry(SceSize args, void *argp)
46{
47 SDL_RunThread(*(SDL_Thread **)argp);
48 return 0;
49}
50
51bool SDL_SYS_CreateThread(SDL_Thread *thread,
52 SDL_FunctionPointer pfnBeginThread,
53 SDL_FunctionPointer pfnEndThread)
54
55{
56 char thread_name[VITA_THREAD_NAME_MAX];
57 size_t stack_size = VITA_THREAD_STACK_SIZE_DEFAULT;
58
59 SDL_strlcpy(thread_name, "SDL thread", VITA_THREAD_NAME_MAX);
60 if (thread->name) {
61 SDL_strlcpy(thread_name, thread->name, VITA_THREAD_NAME_MAX);
62 }
63
64 if (thread->stacksize) {
65 if (thread->stacksize < VITA_THREAD_STACK_SIZE_MIN) {
66 thread->stacksize = VITA_THREAD_STACK_SIZE_MIN;
67 }
68 if (thread->stacksize > VITA_THREAD_STACK_SIZE_MAX) {
69 thread->stacksize = VITA_THREAD_STACK_SIZE_MAX;
70 }
71 stack_size = thread->stacksize;
72 }
73
74 // Create new thread with the same priority as the current thread
75 thread->handle = sceKernelCreateThread(
76 thread_name, // name
77 ThreadEntry, // function to run
78 0, // priority. 0 means priority of calling thread
79 stack_size, // stack size
80 0, // attributes. always 0
81 0, // cpu affinity mask. 0 = all CPUs
82 NULL // opt. always NULL
83 );
84
85 if (thread->handle < 0) {
86 return SDL_SetError("sceKernelCreateThread() failed");
87 }
88
89 sceKernelStartThread(thread->handle, 4, &thread);
90 return true;
91}
92
93void SDL_SYS_SetupThread(const char *name)
94{
95 // Do nothing.
96}
97
98SDL_ThreadID SDL_GetCurrentThreadID(void)
99{
100 return (SDL_ThreadID)sceKernelGetThreadId();
101}
102
103void SDL_SYS_WaitThread(SDL_Thread *thread)
104{
105 sceKernelWaitThreadEnd(thread->handle, NULL, NULL);
106 sceKernelDeleteThread(thread->handle);
107}
108
109void SDL_SYS_DetachThread(SDL_Thread *thread)
110{
111 // Do nothing.
112}
113
114bool SDL_SYS_SetThreadPriority(SDL_ThreadPriority priority)
115{
116 int value = VITA_THREAD_PRIORITY_NORMAL;
117
118 switch (priority) {
119 case SDL_THREAD_PRIORITY_LOW:
120 value = VITA_THREAD_PRIORITY_LOW;
121 break;
122 case SDL_THREAD_PRIORITY_NORMAL:
123 value = VITA_THREAD_PRIORITY_NORMAL;
124 break;
125 case SDL_THREAD_PRIORITY_HIGH:
126 value = VITA_THREAD_PRIORITY_HIGH;
127 break;
128 case SDL_THREAD_PRIORITY_TIME_CRITICAL:
129 value = VITA_THREAD_PRIORITY_TIME_CRITICAL;
130 break;
131 }
132
133 if (sceKernelChangeThreadPriority(0, value) < 0) {
134 return SDL_SetError("sceKernelChangeThreadPriority() failed");
135 }
136 return true;
137}
138
139#endif // SDL_THREAD_VITA
diff --git a/contrib/SDL-3.2.8/src/thread/vita/SDL_systhread_c.h b/contrib/SDL-3.2.8/src/thread/vita/SDL_systhread_c.h
new file mode 100644
index 0000000..b231ec7
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/thread/vita/SDL_systhread_c.h
@@ -0,0 +1,24 @@
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 <psp2/types.h>
23
24typedef SceUID SYS_ThreadHandle;
diff --git a/contrib/SDL-3.2.8/src/thread/windows/SDL_syscond_cv.c b/contrib/SDL-3.2.8/src/thread/windows/SDL_syscond_cv.c
new file mode 100644
index 0000000..b29ef63
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/thread/windows/SDL_syscond_cv.c
@@ -0,0 +1,226 @@
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