Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/sdl/thread/SDL_thread.c
21085 views
1
/*
2
Simple DirectMedia Layer
3
Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
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
31
static SDL_AtomicInt SDL_tls_allocated;
32
static SDL_AtomicInt SDL_tls_id;
33
34
void SDL_InitTLSData(void)
35
{
36
SDL_SYS_InitTLSData();
37
}
38
39
void *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
57
bool 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
} else {
84
// Make sure we don't allocate an ID clobbering this one
85
int tls_id = SDL_GetAtomicInt(&SDL_tls_id);
86
while (storage_index >= tls_id) {
87
if (SDL_CompareAndSwapAtomicInt(&SDL_tls_id, tls_id, storage_index + 1)) {
88
break;
89
}
90
tls_id = SDL_GetAtomicInt(&SDL_tls_id);
91
}
92
}
93
94
// Get the storage for the current thread
95
storage = SDL_SYS_GetTLSData();
96
if (!storage || storage_index >= storage->limit) {
97
unsigned int i, oldlimit, newlimit;
98
SDL_TLSData *new_storage;
99
100
oldlimit = storage ? storage->limit : 0;
101
newlimit = (storage_index + TLS_ALLOC_CHUNKSIZE);
102
new_storage = (SDL_TLSData *)SDL_realloc(storage, sizeof(*storage) + (newlimit - 1) * sizeof(storage->array[0]));
103
if (!new_storage) {
104
return false;
105
}
106
storage = new_storage;
107
storage->limit = newlimit;
108
for (i = oldlimit; i < newlimit; ++i) {
109
storage->array[i].data = NULL;
110
storage->array[i].destructor = NULL;
111
}
112
if (!SDL_SYS_SetTLSData(storage)) {
113
SDL_free(storage);
114
return false;
115
}
116
SDL_AtomicIncRef(&SDL_tls_allocated);
117
}
118
119
storage->array[storage_index].data = SDL_const_cast(void *, value);
120
storage->array[storage_index].destructor = destructor;
121
return true;
122
}
123
124
void SDL_CleanupTLS(void)
125
{
126
SDL_TLSData *storage;
127
128
// Cleanup the storage for the current thread
129
storage = SDL_SYS_GetTLSData();
130
if (storage) {
131
int i;
132
for (i = 0; i < storage->limit; ++i) {
133
if (storage->array[i].destructor) {
134
storage->array[i].destructor(storage->array[i].data);
135
}
136
}
137
SDL_SYS_SetTLSData(NULL);
138
SDL_free(storage);
139
(void)SDL_AtomicDecRef(&SDL_tls_allocated);
140
}
141
}
142
143
void SDL_QuitTLSData(void)
144
{
145
SDL_CleanupTLS();
146
147
if (SDL_GetAtomicInt(&SDL_tls_allocated) == 0) {
148
SDL_SYS_QuitTLSData();
149
} else {
150
// Some thread hasn't called SDL_CleanupTLS()
151
}
152
}
153
154
/* This is a generic implementation of thread-local storage which doesn't
155
require additional OS support.
156
157
It is not especially efficient and doesn't clean up thread-local storage
158
as threads exit. If there is a real OS that doesn't support thread-local
159
storage this implementation should be improved to be production quality.
160
*/
161
162
typedef struct SDL_TLSEntry
163
{
164
SDL_ThreadID thread;
165
SDL_TLSData *storage;
166
struct SDL_TLSEntry *next;
167
} SDL_TLSEntry;
168
169
static SDL_Mutex *SDL_generic_TLS_mutex;
170
static SDL_TLSEntry *SDL_generic_TLS;
171
172
void SDL_Generic_InitTLSData(void)
173
{
174
if (!SDL_generic_TLS_mutex) {
175
SDL_generic_TLS_mutex = SDL_CreateMutex();
176
}
177
}
178
179
SDL_TLSData *SDL_Generic_GetTLSData(void)
180
{
181
SDL_ThreadID thread = SDL_GetCurrentThreadID();
182
SDL_TLSEntry *entry;
183
SDL_TLSData *storage = NULL;
184
185
SDL_LockMutex(SDL_generic_TLS_mutex);
186
for (entry = SDL_generic_TLS; entry; entry = entry->next) {
187
if (entry->thread == thread) {
188
storage = entry->storage;
189
break;
190
}
191
}
192
SDL_UnlockMutex(SDL_generic_TLS_mutex);
193
194
return storage;
195
}
196
197
bool SDL_Generic_SetTLSData(SDL_TLSData *data)
198
{
199
SDL_ThreadID thread = SDL_GetCurrentThreadID();
200
SDL_TLSEntry *prev, *entry;
201
bool result = true;
202
203
SDL_LockMutex(SDL_generic_TLS_mutex);
204
prev = NULL;
205
for (entry = SDL_generic_TLS; entry; entry = entry->next) {
206
if (entry->thread == thread) {
207
if (data) {
208
entry->storage = data;
209
} else {
210
if (prev) {
211
prev->next = entry->next;
212
} else {
213
SDL_generic_TLS = entry->next;
214
}
215
SDL_free(entry);
216
}
217
break;
218
}
219
prev = entry;
220
}
221
if (!entry && data) {
222
entry = (SDL_TLSEntry *)SDL_malloc(sizeof(*entry));
223
if (entry) {
224
entry->thread = thread;
225
entry->storage = data;
226
entry->next = SDL_generic_TLS;
227
SDL_generic_TLS = entry;
228
} else {
229
result = false;
230
}
231
}
232
SDL_UnlockMutex(SDL_generic_TLS_mutex);
233
234
return result;
235
}
236
237
void SDL_Generic_QuitTLSData(void)
238
{
239
SDL_TLSEntry *entry;
240
241
// This should have been cleaned up by the time we get here
242
SDL_assert(!SDL_generic_TLS);
243
if (SDL_generic_TLS) {
244
SDL_LockMutex(SDL_generic_TLS_mutex);
245
for (entry = SDL_generic_TLS; entry; ) {
246
SDL_TLSEntry *next = entry->next;
247
SDL_free(entry->storage);
248
SDL_free(entry);
249
entry = next;
250
}
251
SDL_generic_TLS = NULL;
252
SDL_UnlockMutex(SDL_generic_TLS_mutex);
253
}
254
255
if (SDL_generic_TLS_mutex) {
256
SDL_DestroyMutex(SDL_generic_TLS_mutex);
257
SDL_generic_TLS_mutex = NULL;
258
}
259
}
260
261
// Non-thread-safe global error variable
262
static SDL_error *SDL_GetStaticErrBuf(void)
263
{
264
static SDL_error SDL_global_error;
265
static char SDL_global_error_str[128];
266
SDL_global_error.str = SDL_global_error_str;
267
SDL_global_error.len = sizeof(SDL_global_error_str);
268
return &SDL_global_error;
269
}
270
271
#ifndef SDL_THREADS_DISABLED
272
static void SDLCALL SDL_FreeErrBuf(void *data)
273
{
274
SDL_error *errbuf = (SDL_error *)data;
275
276
if (errbuf->str) {
277
errbuf->free_func(errbuf->str);
278
}
279
errbuf->free_func(errbuf);
280
}
281
#endif
282
283
// Routine to get the thread-specific error variable
284
SDL_error *SDL_GetErrBuf(bool create)
285
{
286
#ifdef SDL_THREADS_DISABLED
287
return SDL_GetStaticErrBuf();
288
#else
289
static SDL_TLSID tls_errbuf;
290
SDL_error *errbuf;
291
292
errbuf = (SDL_error *)SDL_GetTLS(&tls_errbuf);
293
if (!errbuf) {
294
if (!create) {
295
return NULL;
296
}
297
298
/* Get the original memory functions for this allocation because the lifetime
299
* of the error buffer may span calls to SDL_SetMemoryFunctions() by the app
300
*/
301
SDL_realloc_func realloc_func;
302
SDL_free_func free_func;
303
SDL_GetOriginalMemoryFunctions(NULL, NULL, &realloc_func, &free_func);
304
305
errbuf = (SDL_error *)realloc_func(NULL, sizeof(*errbuf));
306
if (!errbuf) {
307
return SDL_GetStaticErrBuf();
308
}
309
SDL_zerop(errbuf);
310
errbuf->realloc_func = realloc_func;
311
errbuf->free_func = free_func;
312
SDL_SetTLS(&tls_errbuf, errbuf, SDL_FreeErrBuf);
313
}
314
return errbuf;
315
#endif // SDL_THREADS_DISABLED
316
}
317
318
static bool ThreadValid(SDL_Thread *thread)
319
{
320
return SDL_ObjectValid(thread, SDL_OBJECT_TYPE_THREAD);
321
}
322
323
void SDL_RunThread(SDL_Thread *thread)
324
{
325
void *userdata = thread->userdata;
326
int(SDLCALL *userfunc)(void *) = thread->userfunc;
327
328
int *statusloc = &thread->status;
329
330
// Perform any system-dependent setup - this function may not fail
331
SDL_SYS_SetupThread(thread->name);
332
333
// Get the thread id
334
thread->threadid = SDL_GetCurrentThreadID();
335
336
// Run the function
337
*statusloc = userfunc(userdata);
338
339
// Clean up thread-local storage
340
SDL_CleanupTLS();
341
342
// Mark us as ready to be joined (or detached)
343
if (!SDL_CompareAndSwapAtomicInt(&thread->state, SDL_THREAD_ALIVE, SDL_THREAD_COMPLETE)) {
344
// Clean up if something already detached us.
345
if (SDL_GetAtomicInt(&thread->state) == SDL_THREAD_DETACHED) {
346
SDL_free(thread->name); // Can't free later, we've already cleaned up TLS
347
SDL_free(thread);
348
}
349
}
350
}
351
352
SDL_Thread *SDL_CreateThreadWithPropertiesRuntime(SDL_PropertiesID props,
353
SDL_FunctionPointer pfnBeginThread,
354
SDL_FunctionPointer pfnEndThread)
355
{
356
// rather than check this in every backend, just make sure it's correct upfront. Only allow non-NULL if Windows, or Microsoft GDK.
357
#if !defined(SDL_PLATFORM_WINDOWS)
358
if (pfnBeginThread || pfnEndThread) {
359
SDL_SetError("_beginthreadex/_endthreadex not supported on this platform");
360
return NULL;
361
}
362
#endif
363
364
SDL_ThreadFunction fn = (SDL_ThreadFunction) SDL_GetPointerProperty(props, SDL_PROP_THREAD_CREATE_ENTRY_FUNCTION_POINTER, NULL);
365
const char *name = SDL_GetStringProperty(props, SDL_PROP_THREAD_CREATE_NAME_STRING, NULL);
366
const size_t stacksize = (size_t) SDL_GetNumberProperty(props, SDL_PROP_THREAD_CREATE_STACKSIZE_NUMBER, 0);
367
void *userdata = SDL_GetPointerProperty(props, SDL_PROP_THREAD_CREATE_USERDATA_POINTER, NULL);
368
369
if (!fn) {
370
SDL_SetError("Thread entry function is NULL");
371
return NULL;
372
}
373
374
SDL_InitMainThread();
375
376
SDL_Thread *thread = (SDL_Thread *)SDL_calloc(1, sizeof(*thread));
377
if (!thread) {
378
return NULL;
379
}
380
thread->status = -1;
381
SDL_SetAtomicInt(&thread->state, SDL_THREAD_ALIVE);
382
383
// Set up the arguments for the thread
384
if (name) {
385
thread->name = SDL_strdup(name);
386
if (!thread->name) {
387
SDL_free(thread);
388
return NULL;
389
}
390
}
391
392
thread->userfunc = fn;
393
thread->userdata = userdata;
394
thread->stacksize = stacksize;
395
396
SDL_SetObjectValid(thread, SDL_OBJECT_TYPE_THREAD, true);
397
398
// Create the thread and go!
399
if (!SDL_SYS_CreateThread(thread, pfnBeginThread, pfnEndThread)) {
400
// Oops, failed. Gotta free everything
401
SDL_SetObjectValid(thread, SDL_OBJECT_TYPE_THREAD, false);
402
SDL_free(thread->name);
403
SDL_free(thread);
404
thread = NULL;
405
}
406
407
// Everything is running now
408
return thread;
409
}
410
411
SDL_Thread *SDL_CreateThreadRuntime(SDL_ThreadFunction fn,
412
const char *name, void *userdata,
413
SDL_FunctionPointer pfnBeginThread,
414
SDL_FunctionPointer pfnEndThread)
415
{
416
const SDL_PropertiesID props = SDL_CreateProperties();
417
SDL_SetPointerProperty(props, SDL_PROP_THREAD_CREATE_ENTRY_FUNCTION_POINTER, (void *) fn);
418
SDL_SetStringProperty(props, SDL_PROP_THREAD_CREATE_NAME_STRING, name);
419
SDL_SetPointerProperty(props, SDL_PROP_THREAD_CREATE_USERDATA_POINTER, userdata);
420
SDL_Thread *thread = SDL_CreateThreadWithPropertiesRuntime(props, pfnBeginThread, pfnEndThread);
421
SDL_DestroyProperties(props);
422
return thread;
423
}
424
425
// internal helper function, not in the public API.
426
SDL_Thread *SDL_CreateThreadWithStackSize(SDL_ThreadFunction fn, const char *name, size_t stacksize, void *userdata)
427
{
428
const SDL_PropertiesID props = SDL_CreateProperties();
429
SDL_SetPointerProperty(props, SDL_PROP_THREAD_CREATE_ENTRY_FUNCTION_POINTER, (void *) fn);
430
SDL_SetStringProperty(props, SDL_PROP_THREAD_CREATE_NAME_STRING, name);
431
SDL_SetPointerProperty(props, SDL_PROP_THREAD_CREATE_USERDATA_POINTER, userdata);
432
SDL_SetNumberProperty(props, SDL_PROP_THREAD_CREATE_STACKSIZE_NUMBER, (Sint64) stacksize);
433
SDL_Thread *thread = SDL_CreateThreadWithProperties(props);
434
SDL_DestroyProperties(props);
435
return thread;
436
}
437
438
SDL_ThreadID SDL_GetThreadID(SDL_Thread *thread)
439
{
440
SDL_ThreadID id = 0;
441
442
if (thread) {
443
if (ThreadValid(thread)) {
444
id = thread->threadid;
445
}
446
} else {
447
id = SDL_GetCurrentThreadID();
448
}
449
return id;
450
}
451
452
const char *SDL_GetThreadName(SDL_Thread *thread)
453
{
454
if (ThreadValid(thread)) {
455
return SDL_GetPersistentString(thread->name);
456
} else {
457
return NULL;
458
}
459
}
460
461
bool SDL_SetCurrentThreadPriority(SDL_ThreadPriority priority)
462
{
463
return SDL_SYS_SetThreadPriority(priority);
464
}
465
466
void SDL_WaitThread(SDL_Thread *thread, int *status)
467
{
468
if (!ThreadValid(thread)) {
469
if (status) {
470
*status = -1;
471
}
472
return;
473
}
474
475
SDL_SYS_WaitThread(thread);
476
if (status) {
477
*status = thread->status;
478
}
479
SDL_SetObjectValid(thread, SDL_OBJECT_TYPE_THREAD, false);
480
SDL_free(thread->name);
481
SDL_free(thread);
482
}
483
484
SDL_ThreadState SDL_GetThreadState(SDL_Thread *thread)
485
{
486
if (!ThreadValid(thread)) {
487
return SDL_THREAD_UNKNOWN;
488
}
489
490
return (SDL_ThreadState)SDL_GetAtomicInt(&thread->state);
491
}
492
493
void SDL_DetachThread(SDL_Thread *thread)
494
{
495
if (!ThreadValid(thread)) {
496
return;
497
}
498
499
// Grab dibs if the state is alive+joinable.
500
if (SDL_CompareAndSwapAtomicInt(&thread->state, SDL_THREAD_ALIVE, SDL_THREAD_DETACHED)) {
501
// The thread may vanish at any time, it's no longer valid
502
SDL_SetObjectValid(thread, SDL_OBJECT_TYPE_THREAD, false);
503
SDL_SYS_DetachThread(thread);
504
} else {
505
// all other states are pretty final, see where we landed.
506
SDL_ThreadState thread_state = SDL_GetThreadState(thread);
507
if (thread_state == SDL_THREAD_DETACHED) {
508
return; // already detached (you shouldn't call this twice!)
509
} else if (thread_state == SDL_THREAD_COMPLETE) {
510
SDL_WaitThread(thread, NULL); // already done, clean it up.
511
}
512
}
513
}
514
515
void SDL_WaitSemaphore(SDL_Semaphore *sem)
516
{
517
SDL_WaitSemaphoreTimeoutNS(sem, -1);
518
}
519
520
bool SDL_TryWaitSemaphore(SDL_Semaphore *sem)
521
{
522
return SDL_WaitSemaphoreTimeoutNS(sem, 0);
523
}
524
525
bool SDL_WaitSemaphoreTimeout(SDL_Semaphore *sem, Sint32 timeoutMS)
526
{
527
Sint64 timeoutNS;
528
529
if (timeoutMS >= 0) {
530
timeoutNS = SDL_MS_TO_NS(timeoutMS);
531
} else {
532
timeoutNS = -1;
533
}
534
return SDL_WaitSemaphoreTimeoutNS(sem, timeoutNS);
535
}
536
537
void SDL_WaitCondition(SDL_Condition *cond, SDL_Mutex *mutex)
538
{
539
SDL_WaitConditionTimeoutNS(cond, mutex, -1);
540
}
541
542
bool SDL_WaitConditionTimeout(SDL_Condition *cond, SDL_Mutex *mutex, Sint32 timeoutMS)
543
{
544
Sint64 timeoutNS;
545
546
if (timeoutMS >= 0) {
547
timeoutNS = SDL_MS_TO_NS(timeoutMS);
548
} else {
549
timeoutNS = -1;
550
}
551
return SDL_WaitConditionTimeoutNS(cond, mutex, timeoutNS);
552
}
553
554
bool SDL_ShouldInit(SDL_InitState *state)
555
{
556
while (SDL_GetAtomicInt(&state->status) != SDL_INIT_STATUS_INITIALIZED) {
557
if (SDL_CompareAndSwapAtomicInt(&state->status, SDL_INIT_STATUS_UNINITIALIZED, SDL_INIT_STATUS_INITIALIZING)) {
558
state->thread = SDL_GetCurrentThreadID();
559
return true;
560
}
561
562
// Wait for the other thread to complete transition
563
SDL_Delay(1);
564
}
565
return false;
566
}
567
568
bool SDL_ShouldQuit(SDL_InitState *state)
569
{
570
while (SDL_GetAtomicInt(&state->status) != SDL_INIT_STATUS_UNINITIALIZED) {
571
if (SDL_CompareAndSwapAtomicInt(&state->status, SDL_INIT_STATUS_INITIALIZED, SDL_INIT_STATUS_UNINITIALIZING)) {
572
state->thread = SDL_GetCurrentThreadID();
573
return true;
574
}
575
576
// Wait for the other thread to complete transition
577
SDL_Delay(1);
578
}
579
return false;
580
}
581
582
void SDL_SetInitialized(SDL_InitState *state, bool initialized)
583
{
584
SDL_assert(state->thread == SDL_GetCurrentThreadID());
585
586
if (initialized) {
587
SDL_SetAtomicInt(&state->status, SDL_INIT_STATUS_INITIALIZED);
588
} else {
589
SDL_SetAtomicInt(&state->status, SDL_INIT_STATUS_UNINITIALIZED);
590
}
591
}
592
593
594