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 
40 void *
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 
52 int
53 SDL_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 
86 static 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 
113 typedef struct SDL_TLSEntry {
117 } SDL_TLSEntry;
118 
121 
122 
123 SDL_TLSData *
125 {
127  SDL_TLSEntry *entry;
129 
130 #if !SDL_THREADS_DISABLED
131  if (!SDL_generic_TLS_mutex) {
132  static SDL_SpinLock tls_lock;
133  SDL_AtomicLock(&tls_lock);
134  if (!SDL_generic_TLS_mutex) {
138  if (!SDL_generic_TLS_mutex) {
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 
162 int
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 */
205 SDL_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 */
256 typedef struct
257 {
258  int (SDLCALL * func) (void *);
259  void *data;
261  SDL_sem *wait;
262 } thread_args;
263 
264 void
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 */
286  SDL_TLSCleanup();
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  }
295  SDL_free(thread);
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
310 SDL_Thread *
311 SDL_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
316 SDL_Thread *
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) {
328  SDL_OutOfMemory();
329  return (NULL);
330  }
331  SDL_zerop(thread);
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) {
339  SDL_OutOfMemory();
340  SDL_free(thread);
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) {
348  SDL_OutOfMemory();
349  if (thread->name) {
350  SDL_free(thread->name);
351  }
352  SDL_free(thread);
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  }
363  SDL_free(thread);
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  }
384  SDL_free(thread);
385  thread = NULL;
386  }
387  SDL_DestroySemaphore(args->wait);
388  SDL_free(args);
389 
390  /* Everything is running now */
391  return (thread);
392 }
393 
394 #ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
396 SDL_CreateThread(int (SDLCALL * fn) (void *),
397  const char *name, void *data,
398  pfnSDL_CurrentBeginThread pfnBeginThread,
399  pfnSDL_CurrentEndThread pfnEndThread)
400 #else
402 SDL_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 
428 SDL_Thread *
429 SDL_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 
451 const char *
453 {
454  if (thread) {
455  return thread->name;
456  } else {
457  return NULL;
458  }
459 }
460 
461 int
463 {
464  return SDL_SYS_SetThreadPriority(priority);
465 }
466 
467 void
469 {
470  if (thread) {
472  if (status) {
473  *status = thread->status;
474  }
475  if (thread->name) {
476  SDL_free(thread->name);
477  }
478  SDL_free(thread);
479  }
480 }
481 
482 void
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: */
SDL_TLSData
Definition: SDL_thread_c.h:70
SDL_HINT_THREAD_STACK_SIZE
#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
SDL_THREAD_STATE_DETACHED
Definition: SDL_thread_c.h:48
SDL_DetachThread
void SDL_DetachThread(SDL_Thread *thread)
Definition: SDL_thread.c:483
DECLSPEC
#define DECLSPEC
Definition: SDL_internal.h:48
SDL_systhread.h
SDL_Generic_SetTLSData
int SDL_Generic_SetTLSData(SDL_TLSData *storage)
Definition: SDL_thread.c:163
SDL_THREAD_STATE_ALIVE
Definition: SDL_thread_c.h:47
thread_args::data
void * data
Definition: SDL_thread.c:259
TLS_ALLOC_CHUNKSIZE
#define TLS_ALLOC_CHUNKSIZE
Definition: SDL_thread_c.h:79
SDL_TLSData::destructor
void(* destructor)(void *)
Definition: SDL_thread_c.h:74
SDL_CreateSemaphore
#define SDL_CreateSemaphore
Definition: SDL_dynapi_overrides.h:264
SDL_AtomicCAS
#define SDL_AtomicCAS
Definition: SDL_dynapi_overrides.h:66
SDL_LockMutex
#define SDL_LockMutex
Definition: SDL_dynapi_overrides.h:260
SDL_CreateThread
#define SDL_CreateThread
Definition: SDL_thread.c:305
NULL
#define NULL
Definition: begin_code.h:167
SDL_RunThread
void SDL_RunThread(void *data)
Definition: SDL_thread.c:265
SDL_MemoryBarrierRelease
#define SDL_MemoryBarrierRelease()
Definition: SDL_atomic.h:207
SDL_zerop
#define SDL_zerop(x)
Definition: SDL_stdinc.h:417
SDL_AtomicLock
#define SDL_AtomicLock
Definition: SDL_dynapi_overrides.h:64
SDL_mutex
Definition: SDL_sysmutex.c:29
SDL_const_cast
#define SDL_const_cast(type, expression)
Definition: SDL_stdinc.h:139
mutex
static SDL_mutex * mutex
Definition: testlock.c:23
SDLCALL
#define SDLCALL
Definition: SDL_internal.h:49
SDL_SYS_SetupThread
void SDL_SYS_SetupThread(const char *name)
Definition: SDL_systhread.c:42
SDL_realloc
#define SDL_realloc
Definition: SDL_dynapi_overrides.h:376
SDL_InvalidParamError
#define SDL_InvalidParamError(param)
Definition: SDL_error.h:54
SDL_AtomicIncRef
#define SDL_AtomicIncRef(a)
Increment an atomic variable used as a reference count.
Definition: SDL_atomic.h:252
SDL_error
Definition: SDL_error_c.h:33
SDL_CreateMutex
#define SDL_CreateMutex
Definition: SDL_dynapi_overrides.h:259
SDL_SYS_WaitThread
void SDL_SYS_WaitThread(SDL_Thread *thread)
Definition: SDL_systhread.c:60
SDL_Thread
Definition: SDL_thread_c.h:54
SDL_GetHint
#define SDL_GetHint
Definition: SDL_dynapi_overrides.h:191
SDL_SYS_CreateThread
int SDL_SYS_CreateThread(SDL_Thread *thread, void *args)
Definition: SDL_systhread.c:35
func
GLenum func
Definition: SDL_opengl_glext.h:657
SDL_THREAD_STATE_CLEANED
Definition: SDL_thread_c.h:50
SDL_SemPost
#define SDL_SemPost
Definition: SDL_dynapi_overrides.h:269
SDL_TLSID
unsigned int SDL_TLSID
Definition: SDL_thread.h:52
data
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: SDL_opengl.h:1973
SDL_SYS_SetTLSData
int SDL_SYS_SetTLSData(SDL_TLSData *data)
Definition: SDL_systls.c:33
SDL_TLSData::array
struct SDL_TLSData::@39 array[1]
thread_args::func
int(* func)(void *)
Definition: SDL_thread.c:258
SDL_TLSEntry::thread
SDL_threadID thread
Definition: SDL_thread.c:114
SDL_thread.h
SDL_AtomicUnlock
#define SDL_AtomicUnlock
Definition: SDL_dynapi_overrides.h:65
SDL_CreateThreadInternal
SDL_Thread * SDL_CreateThreadInternal(int(*fn)(void *), const char *name, const size_t stacksize, void *data)
Definition: SDL_thread.c:429
SDL_CreateThreadWithStackSize
#define SDL_CreateThreadWithStackSize
Definition: SDL_thread.c:306
SDL_free
#define SDL_free
Definition: SDL_dynapi_overrides.h:377
SDL_FALSE
Definition: SDL_stdinc.h:163
name
GLuint const GLchar * name
Definition: SDL_opengl_glext.h:660
SDL_GetErrBuf
SDL_error * SDL_GetErrBuf(void)
Definition: SDL_thread.c:206
thread_args::info
SDL_Thread * info
Definition: SDL_thread.c:260
SDL_SYS_DetachThread
void SDL_SYS_DetachThread(SDL_Thread *thread)
Definition: SDL_systhread.c:66
SDL_assert.h
SDL_TLSCreate
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
SDL_SYS_GetTLSData
SDL_TLSData * SDL_SYS_GetTLSData(void)
Definition: SDL_systls.c:27
SDL_SYS_SetThreadPriority
int SDL_SYS_SetThreadPriority(SDL_ThreadPriority priority)
Definition: SDL_systhread.c:54
SDL_GetThreadName
const char * SDL_GetThreadName(SDL_Thread *thread)
Definition: SDL_thread.c:452
SDL_assert
#define SDL_assert(condition)
Definition: SDL_assert.h:169
SDL_thread_c.h
SDL_SpinLock
int SDL_SpinLock
Definition: SDL_atomic.h:89
SDL_OutOfMemory
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
id
GLuint id
Definition: SDL_opengl_glext.h:528
SDL_TLSEntry::storage
SDL_TLSData * storage
Definition: SDL_thread.c:115
SDL_bool
SDL_bool
Definition: SDL_stdinc.h:161
SDL_threadID
unsigned long SDL_threadID
Definition: SDL_thread.h:49
SDL_SemWait
#define SDL_SemWait
Definition: SDL_dynapi_overrides.h:266
SDL_TLSData::data
void * data
Definition: SDL_thread_c.h:73
thread_args
Definition: SDL_thread.c:256
value
GLsizei const GLfloat * value
Definition: SDL_opengl_glext.h:698
SDL_TRUE
Definition: SDL_stdinc.h:164
SDL_TLSData::limit
unsigned int limit
Definition: SDL_thread_c.h:71
SDL_MemoryBarrierAcquire
#define SDL_MemoryBarrierAcquire()
Definition: SDL_atomic.h:208
SDL_hints.h
SDL_atomic_t
A type representing an atomic integer value. It is a struct so people don't accidentally use numeric ...
Definition: SDL_atomic.h:216
SDL_Generic_GetTLSData
SDL_TLSData * SDL_Generic_GetTLSData(void)
Definition: SDL_thread.c:124
SDL_WaitThread
void SDL_WaitThread(SDL_Thread *thread, int *status)
Definition: SDL_thread.c:468
thread_args::wait
SDL_sem * wait
Definition: SDL_thread.c:261
SDL_strdup
#define SDL_strdup
Definition: SDL_dynapi_overrides.h:397
SDL_DestroySemaphore
#define SDL_DestroySemaphore
Definition: SDL_dynapi_overrides.h:265
SDL_TLSGet
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_TLSEntry::next
struct SDL_TLSEntry * next
Definition: SDL_thread.c:116
SDL_AtomicSet
#define SDL_AtomicSet
Definition: SDL_dynapi_overrides.h:67
SDL_malloc
#define SDL_malloc
Definition: SDL_dynapi_overrides.h:374
SDL_AtomicGet
#define SDL_AtomicGet
Definition: SDL_dynapi_overrides.h:68
SDL_TLSCleanup
static void SDL_TLSCleanup()
Definition: SDL_thread.c:87
SDL_UnlockMutex
#define SDL_UnlockMutex
Definition: SDL_dynapi_overrides.h:262
SDL_ThreadPriority
SDL_ThreadPriority
Definition: SDL_thread.h:59
size_t
unsigned int size_t
Definition: SDL_config_windows.h:68
SDL_strtoll
#define SDL_strtoll
Definition: SDL_dynapi_overrides.h:414
i
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
SDL_ThreadID
#define SDL_ThreadID
Definition: SDL_dynapi_overrides.h:475
SDL_generic_TLS_mutex
static SDL_mutex * SDL_generic_TLS_mutex
Definition: SDL_thread.c:119
SDL_GetThreadID
SDL_threadID SDL_GetThreadID(SDL_Thread *thread)
Definition: SDL_thread.c:439
Sint64
int64_t Sint64
Definition: SDL_stdinc.h:210
SDL_TLSSet
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
SDL_SetThreadPriority
int SDL_SetThreadPriority(SDL_ThreadPriority priority)
Definition: SDL_thread.c:462
SDL_THREAD_STATE_ZOMBIE
Definition: SDL_thread_c.h:49
SDL_generic_TLS
static SDL_TLSEntry * SDL_generic_TLS
Definition: SDL_thread.c:120
SDL_TLSEntry
Definition: SDL_thread.c:113