SDL 2.0
SDL_thread.c
Go to the documentation of this file.
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2019 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_assert.h"
26#include "SDL_thread.h"
27#include "SDL_thread_c.h"
28#include "SDL_systhread.h"
29#include "SDL_hints.h"
30#include "../SDL_error_c.h"
31
32
35{
36 static SDL_atomic_t SDL_tls_id;
37 return SDL_AtomicIncRef(&SDL_tls_id)+1;
38}
39
40void *
42{
43 SDL_TLSData *storage;
44
45 storage = SDL_SYS_GetTLSData();
46 if (!storage || id == 0 || id > storage->limit) {
47 return NULL;
48 }
49 return storage->array[id-1].data;
50}
51
52int
53SDL_TLSSet(SDL_TLSID id, const void *value, void (SDLCALL *destructor)(void *))
54{
55 SDL_TLSData *storage;
56
57 if (id == 0) {
58 return SDL_InvalidParamError("id");
59 }
60
61 storage = SDL_SYS_GetTLSData();
62 if (!storage || (id > storage->limit)) {
63 unsigned int i, oldlimit, newlimit;
64
65 oldlimit = storage ? storage->limit : 0;
66 newlimit = (id + TLS_ALLOC_CHUNKSIZE);
67 storage = (SDL_TLSData *)SDL_realloc(storage, sizeof(*storage)+(newlimit-1)*sizeof(storage->array[0]));
68 if (!storage) {
69 return SDL_OutOfMemory();
70 }
71 storage->limit = newlimit;
72 for (i = oldlimit; i < newlimit; ++i) {
73 storage->array[i].data = NULL;
74 storage->array[i].destructor = NULL;
75 }
76 if (SDL_SYS_SetTLSData(storage) != 0) {
77 return -1;
78 }
79 }
80
81 storage->array[id-1].data = SDL_const_cast(void*, value);
82 storage->array[id-1].destructor = destructor;
83 return 0;
84}
85
86static void
88{
89 SDL_TLSData *storage;
90
91 storage = SDL_SYS_GetTLSData();
92 if (storage) {
93 unsigned int i;
94 for (i = 0; i < storage->limit; ++i) {
95 if (storage->array[i].destructor) {
96 storage->array[i].destructor(storage->array[i].data);
97 }
98 }
100 SDL_free(storage);
101 }
102}
103
104
105/* This is a generic implementation of thread-local storage which doesn't
106 require additional OS support.
107
108 It is not especially efficient and doesn't clean up thread-local storage
109 as threads exit. If there is a real OS that doesn't support thread-local
110 storage this implementation should be improved to be production quality.
111*/
112
113typedef struct SDL_TLSEntry {
118
121
122
125{
127 SDL_TLSEntry *entry;
129
130#if !SDL_THREADS_DISABLED
132 static SDL_SpinLock tls_lock;
133 SDL_AtomicLock(&tls_lock);
139 SDL_AtomicUnlock(&tls_lock);
140 return NULL;
141 }
142 }
143 SDL_AtomicUnlock(&tls_lock);
144 }
145#endif /* SDL_THREADS_DISABLED */
146
149 for (entry = SDL_generic_TLS; entry; entry = entry->next) {
150 if (entry->thread == thread) {
151 storage = entry->storage;
152 break;
153 }
154 }
155#if !SDL_THREADS_DISABLED
157#endif
158
159 return storage;
160}
161
162int
164{
166 SDL_TLSEntry *prev, *entry;
167
168 /* SDL_Generic_GetTLSData() is always called first, so we can assume SDL_generic_TLS_mutex */
170 prev = NULL;
171 for (entry = SDL_generic_TLS; entry; entry = entry->next) {
172 if (entry->thread == thread) {
173 if (storage) {
174 entry->storage = storage;
175 } else {
176 if (prev) {
177 prev->next = entry->next;
178 } else {
179 SDL_generic_TLS = entry->next;
180 }
181 SDL_free(entry);
182 }
183 break;
184 }
185 prev = entry;
186 }
187 if (!entry) {
188 entry = (SDL_TLSEntry *)SDL_malloc(sizeof(*entry));
189 if (entry) {
190 entry->thread = thread;
191 entry->storage = storage;
192 entry->next = SDL_generic_TLS;
193 SDL_generic_TLS = entry;
194 }
195 }
197
198 if (!entry) {
199 return SDL_OutOfMemory();
200 }
201 return 0;
202}
203
204/* Routine to get the thread-specific error variable */
205SDL_error *
207{
208 static SDL_SpinLock tls_lock;
209 static SDL_bool tls_being_created;
210 static SDL_TLSID tls_errbuf;
211 static SDL_error SDL_global_errbuf;
212 const SDL_error *ALLOCATION_IN_PROGRESS = (SDL_error *)-1;
213 SDL_error *errbuf;
214
215 /* tls_being_created is there simply to prevent recursion if SDL_TLSCreate() fails.
216 It also means it's possible for another thread to also use SDL_global_errbuf,
217 but that's very unlikely and hopefully won't cause issues.
218 */
219 if (!tls_errbuf && !tls_being_created) {
220 SDL_AtomicLock(&tls_lock);
221 if (!tls_errbuf) {
222 SDL_TLSID slot;
223 tls_being_created = SDL_TRUE;
224 slot = SDL_TLSCreate();
225 tls_being_created = SDL_FALSE;
227 tls_errbuf = slot;
228 }
229 SDL_AtomicUnlock(&tls_lock);
230 }
231 if (!tls_errbuf) {
232 return &SDL_global_errbuf;
233 }
234
236 errbuf = (SDL_error *)SDL_TLSGet(tls_errbuf);
237 if (errbuf == ALLOCATION_IN_PROGRESS) {
238 return &SDL_global_errbuf;
239 }
240 if (!errbuf) {
241 /* Mark that we're in the middle of allocating our buffer */
242 SDL_TLSSet(tls_errbuf, ALLOCATION_IN_PROGRESS, NULL);
243 errbuf = (SDL_error *)SDL_malloc(sizeof(*errbuf));
244 if (!errbuf) {
245 SDL_TLSSet(tls_errbuf, NULL, NULL);
246 return &SDL_global_errbuf;
247 }
248 SDL_zerop(errbuf);
249 SDL_TLSSet(tls_errbuf, errbuf, SDL_free);
250 }
251 return errbuf;
252}
253
254
255/* Arguments and callback to setup and run the user thread function */
256typedef struct
257{
258 int (SDLCALL * func) (void *);
259 void *data;
261 SDL_sem *wait;
263
264void
266{
267 thread_args *args = (thread_args *) data;
268 int (SDLCALL * userfunc) (void *) = args->func;
269 void *userdata = args->data;
270 SDL_Thread *thread = args->info;
271 int *statusloc = &thread->status;
272
273 /* Perform any system-dependent setup - this function may not fail */
275
276 /* Get the thread id */
277 thread->threadid = SDL_ThreadID();
278
279 /* Wake up the parent thread */
280 SDL_SemPost(args->wait);
281
282 /* Run the function */
283 *statusloc = userfunc(userdata);
284
285 /* Clean up thread-local storage */
287
288 /* Mark us as ready to be joined (or detached) */
290 /* Clean up if something already detached us. */
292 if (thread->name) {
293 SDL_free(thread->name);
294 }
296 }
297 }
298}
299
300#ifdef SDL_CreateThread
301#undef SDL_CreateThread
302#undef SDL_CreateThreadWithStackSize
303#endif
304#if SDL_DYNAMIC_API
305#define SDL_CreateThread SDL_CreateThread_REAL
306#define SDL_CreateThreadWithStackSize SDL_CreateThreadWithStackSize_REAL
307#endif
308
309#ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
311SDL_CreateThreadWithStackSize(int (SDLCALL * fn) (void *),
312 const char *name, const size_t stacksize, void *data,
313 pfnSDL_CurrentBeginThread pfnBeginThread,
314 pfnSDL_CurrentEndThread pfnEndThread)
315#else
318 const char *name, const size_t stacksize, void *data)
319#endif
320{
322 thread_args *args;
323 int ret;
324
325 /* Allocate memory for the thread info structure */
326 thread = (SDL_Thread *) SDL_malloc(sizeof(*thread));
327 if (thread == NULL) {
329 return (NULL);
330 }
332 thread->status = -1;
334
335 /* Set up the arguments for the thread */
336 if (name != NULL) {
337 thread->name = SDL_strdup(name);
338 if (thread->name == NULL) {
341 return (NULL);
342 }
343 }
344
345 /* Set up the arguments for the thread */
346 args = (thread_args *) SDL_malloc(sizeof(*args));
347 if (args == NULL) {
349 if (thread->name) {
350 SDL_free(thread->name);
351 }
353 return (NULL);
354 }
355 args->func = fn;
356 args->data = data;
357 args->info = thread;
358 args->wait = SDL_CreateSemaphore(0);
359 if (args->wait == NULL) {
360 if (thread->name) {
361 SDL_free(thread->name);
362 }
364 SDL_free(args);
365 return (NULL);
366 }
367
368 thread->stacksize = stacksize;
369
370 /* Create the thread and go! */
371#ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
372 ret = SDL_SYS_CreateThread(thread, args, pfnBeginThread, pfnEndThread);
373#else
374 ret = SDL_SYS_CreateThread(thread, args);
375#endif
376 if (ret >= 0) {
377 /* Wait for the thread function to use arguments */
378 SDL_SemWait(args->wait);
379 } else {
380 /* Oops, failed. Gotta free everything */
381 if (thread->name) {
382 SDL_free(thread->name);
383 }
385 thread = NULL;
386 }
388 SDL_free(args);
389
390 /* Everything is running now */
391 return (thread);
392}
393
394#ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
396SDL_CreateThread(int (SDLCALL * fn) (void *),
397 const char *name, void *data,
398 pfnSDL_CurrentBeginThread pfnBeginThread,
399 pfnSDL_CurrentEndThread pfnEndThread)
400#else
402SDL_CreateThread(int (SDLCALL * fn) (void *),
403 const char *name, void *data)
404#endif
405{
406 /* !!! FIXME: in 2.1, just make stackhint part of the usual API. */
407 const char *stackhint = SDL_GetHint(SDL_HINT_THREAD_STACK_SIZE);
408 size_t stacksize = 0;
409
410 /* If the SDL_HINT_THREAD_STACK_SIZE exists, use it */
411 if (stackhint != NULL) {
412 char *endp = NULL;
413 const Sint64 hintval = SDL_strtoll(stackhint, &endp, 10);
414 if ((*stackhint != '\0') && (*endp == '\0')) { /* a valid number? */
415 if (hintval > 0) { /* reject bogus values. */
416 stacksize = (size_t) hintval;
417 }
418 }
419 }
420
421#ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
422 return SDL_CreateThreadWithStackSize(fn, name, stacksize, data, pfnBeginThread, pfnEndThread);
423#else
424 return SDL_CreateThreadWithStackSize(fn, name, stacksize, data);
425#endif
426}
427
429SDL_CreateThreadInternal(int (SDLCALL * fn) (void *), const char *name,
430 const size_t stacksize, void *data) {
431#ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
432 return SDL_CreateThreadWithStackSize(fn, name, stacksize, data, NULL, NULL);
433#else
434 return SDL_CreateThreadWithStackSize(fn, name, stacksize, data);
435#endif
436}
437
440{
442
443 if (thread) {
444 id = thread->threadid;
445 } else {
446 id = SDL_ThreadID();
447 }
448 return id;
449}
450
451const char *
453{
454 if (thread) {
455 return thread->name;
456 } else {
457 return NULL;
458 }
459}
460
461int
463{
464 return SDL_SYS_SetThreadPriority(priority);
465}
466
467void
469{
470 if (thread) {
472 if (status) {
473 *status = thread->status;
474 }
475 if (thread->name) {
476 SDL_free(thread->name);
477 }
479 }
480}
481
482void
484{
485 if (!thread) {
486 return;
487 }
488
489 /* Grab dibs if the state is alive+joinable. */
492 } else {
493 /* all other states are pretty final, see where we landed. */
494 const int thread_state = SDL_AtomicGet(&thread->state);
495 if ((thread_state == SDL_THREAD_STATE_DETACHED) || (thread_state == SDL_THREAD_STATE_CLEANED)) {
496 return; /* already detached (you shouldn't call this twice!) */
497 } else if (thread_state == SDL_THREAD_STATE_ZOMBIE) {
498 SDL_WaitThread(thread, NULL); /* already done, clean it up. */
499 } else {
500 SDL_assert(0 && "Unexpected thread state");
501 }
502 }
503}
504
505/* vi: set ts=4 sw=4 expandtab: */
#define SDL_assert(condition)
Definition: SDL_assert.h:169
#define SDL_MemoryBarrierRelease()
Definition: SDL_atomic.h:207
int SDL_SpinLock
Definition: SDL_atomic.h:89
#define SDL_AtomicIncRef(a)
Increment an atomic variable used as a reference count.
Definition: SDL_atomic.h:252
#define SDL_MemoryBarrierAcquire()
Definition: SDL_atomic.h:208
unsigned int size_t
#define SDL_AtomicSet
#define SDL_AtomicLock
#define SDL_AtomicCAS
#define SDL_SemPost
#define SDL_SemWait
#define SDL_LockMutex
#define SDL_strtoll
#define SDL_malloc
#define SDL_ThreadID
#define SDL_realloc
#define SDL_DestroySemaphore
#define SDL_CreateMutex
#define SDL_AtomicUnlock
#define SDL_free
#define SDL_strdup
#define SDL_CreateSemaphore
#define SDL_AtomicGet
#define SDL_GetHint
#define SDL_UnlockMutex
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
#define SDL_InvalidParamError(param)
Definition: SDL_error.h:54
#define SDL_HINT_THREAD_STACK_SIZE
A string specifying SDL's threads stack size in bytes or "0" for the backend's default size.
Definition: SDL_hints.h:647
#define SDLCALL
Definition: SDL_internal.h:49
#define DECLSPEC
Definition: SDL_internal.h:48
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: SDL_opengl.h:1974
GLenum func
GLuint id
GLuint const GLchar * name
GLsizei const GLfloat * value
SDL_bool
Definition: SDL_stdinc.h:162
@ SDL_TRUE
Definition: SDL_stdinc.h:164
@ SDL_FALSE
Definition: SDL_stdinc.h:163
#define SDL_zerop(x)
Definition: SDL_stdinc.h:417
#define SDL_const_cast(type, expression)
Definition: SDL_stdinc.h:139
int64_t Sint64
Definition: SDL_stdinc.h:210
void * SDL_TLSGet(SDL_TLSID id)
Get the value associated with a thread local storage ID for the current thread.
Definition: SDL_thread.c:41
SDL_error * SDL_GetErrBuf(void)
Definition: SDL_thread.c:206
int SDL_Generic_SetTLSData(SDL_TLSData *storage)
Definition: SDL_thread.c:163
#define SDL_CreateThread
Definition: SDL_thread.c:305
SDL_TLSID SDL_TLSCreate()
Create an identifier that is globally visible to all threads but refers to data that is thread-specif...
Definition: SDL_thread.c:34
static SDL_TLSEntry * SDL_generic_TLS
Definition: SDL_thread.c:120
void SDL_DetachThread(SDL_Thread *thread)
Definition: SDL_thread.c:483
SDL_threadID SDL_GetThreadID(SDL_Thread *thread)
Definition: SDL_thread.c:439
static SDL_mutex * SDL_generic_TLS_mutex
Definition: SDL_thread.c:119
SDL_Thread * SDL_CreateThreadInternal(int(*fn)(void *), const char *name, const size_t stacksize, void *data)
Definition: SDL_thread.c:429
const char * SDL_GetThreadName(SDL_Thread *thread)
Definition: SDL_thread.c:452
static void SDL_TLSCleanup()
Definition: SDL_thread.c:87
void SDL_RunThread(void *data)
Definition: SDL_thread.c:265
int SDL_SetThreadPriority(SDL_ThreadPriority priority)
Definition: SDL_thread.c:462
SDL_TLSData * SDL_Generic_GetTLSData(void)
Definition: SDL_thread.c:124
void SDL_WaitThread(SDL_Thread *thread, int *status)
Definition: SDL_thread.c:468
#define SDL_CreateThreadWithStackSize
Definition: SDL_thread.c:306
int SDL_TLSSet(SDL_TLSID id, const void *value, void(*destructor)(void *))
Set the value associated with a thread local storage ID for the current thread.
Definition: SDL_thread.c:53
unsigned int SDL_TLSID
Definition: SDL_thread.h:52
unsigned long SDL_threadID
Definition: SDL_thread.h:49
SDL_ThreadPriority
Definition: SDL_thread.h:59
#define TLS_ALLOC_CHUNKSIZE
Definition: SDL_thread_c.h:79
@ SDL_THREAD_STATE_ZOMBIE
Definition: SDL_thread_c.h:49
@ SDL_THREAD_STATE_CLEANED
Definition: SDL_thread_c.h:50
@ SDL_THREAD_STATE_ALIVE
Definition: SDL_thread_c.h:47
@ SDL_THREAD_STATE_DETACHED
Definition: SDL_thread_c.h:48
return Display return Display Bool Bool int int int return Display XEvent Bool(*) XPointer return Display return Display Drawable _Xconst char unsigned int unsigned int return Display Pixmap Pixmap XColor XColor unsigned int unsigned int return Display _Xconst char char int char return Display Visual unsigned int int int char unsigned int unsigned int in i)
Definition: SDL_x11sym.h:50
#define NULL
Definition: begin_code.h:167
void SDL_SYS_SetupThread(const char *name)
Definition: SDL_systhread.c:42
void SDL_SYS_DetachThread(SDL_Thread *thread)
Definition: SDL_systhread.c:66
int SDL_SYS_SetThreadPriority(SDL_ThreadPriority priority)
Definition: SDL_systhread.c:54
void SDL_SYS_WaitThread(SDL_Thread *thread)
Definition: SDL_systhread.c:60
int SDL_SYS_CreateThread(SDL_Thread *thread, void *args)
Definition: SDL_systhread.c:35
SDL_TLSData * SDL_SYS_GetTLSData(void)
Definition: SDL_systls.c:27
int SDL_SYS_SetTLSData(SDL_TLSData *data)
Definition: SDL_systls.c:33
void * data
Definition: SDL_thread_c.h:73
unsigned int limit
Definition: SDL_thread_c.h:71
void(* destructor)(void *)
Definition: SDL_thread_c.h:74
struct SDL_TLSData::@39 array[1]
Definition: SDL_thread.c:113
SDL_TLSData * storage
Definition: SDL_thread.c:115
SDL_threadID thread
Definition: SDL_thread.c:114
struct SDL_TLSEntry * next
Definition: SDL_thread.c:116
A type representing an atomic integer value. It is a struct so people don't accidentally use numeric ...
Definition: SDL_atomic.h:216
SDL_Thread * info
Definition: SDL_thread.c:260
SDL_sem * wait
Definition: SDL_thread.c:261
void * data
Definition: SDL_thread.c:259
int(* func)(void *)
Definition: SDL_thread.c:258
static SDL_mutex * mutex
Definition: testlock.c:23