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