SDL  2.0
SDL_thread.c
Go to the documentation of this file.
1 /*
2  Simple DirectMedia Layer
3  Copyright (C) 1997-2018 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) {
137  SDL_generic_TLS_mutex = 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 
148  SDL_LockMutex(SDL_generic_TLS_mutex);
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
156  SDL_UnlockMutex(SDL_generic_TLS_mutex);
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 */
169  SDL_LockMutex(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  }
196  SDL_UnlockMutex(SDL_generic_TLS_mutex);
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 #endif
303 #if SDL_DYNAMIC_API
304 #define SDL_CreateThread SDL_CreateThread_REAL
305 #endif
306 
307 #ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
308 static SDL_Thread *
309 SDL_CreateThreadWithStackSize(int (SDLCALL * fn) (void *),
310  const char *name, const size_t stacksize, void *data,
311  pfnSDL_CurrentBeginThread pfnBeginThread,
312  pfnSDL_CurrentEndThread pfnEndThread)
313 #else
314 static SDL_Thread *
316  const char *name, const size_t stacksize, void *data)
317 #endif
318 {
320  thread_args *args;
321  int ret;
322 
323  /* Allocate memory for the thread info structure */
324  thread = (SDL_Thread *) SDL_malloc(sizeof(*thread));
325  if (thread == NULL) {
326  SDL_OutOfMemory();
327  return (NULL);
328  }
329  SDL_zerop(thread);
330  thread->status = -1;
332 
333  /* Set up the arguments for the thread */
334  if (name != NULL) {
335  thread->name = SDL_strdup(name);
336  if (thread->name == NULL) {
337  SDL_OutOfMemory();
338  SDL_free(thread);
339  return (NULL);
340  }
341  }
342 
343  /* Set up the arguments for the thread */
344  args = (thread_args *) SDL_malloc(sizeof(*args));
345  if (args == NULL) {
346  SDL_OutOfMemory();
347  if (thread->name) {
348  SDL_free(thread->name);
349  }
350  SDL_free(thread);
351  return (NULL);
352  }
353  args->func = fn;
354  args->data = data;
355  args->info = thread;
356  args->wait = SDL_CreateSemaphore(0);
357  if (args->wait == NULL) {
358  if (thread->name) {
359  SDL_free(thread->name);
360  }
361  SDL_free(thread);
362  SDL_free(args);
363  return (NULL);
364  }
365 
366  thread->stacksize = stacksize;
367 
368  /* Create the thread and go! */
369 #ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
370  ret = SDL_SYS_CreateThread(thread, args, pfnBeginThread, pfnEndThread);
371 #else
372  ret = SDL_SYS_CreateThread(thread, args);
373 #endif
374  if (ret >= 0) {
375  /* Wait for the thread function to use arguments */
376  SDL_SemWait(args->wait);
377  } else {
378  /* Oops, failed. Gotta free everything */
379  if (thread->name) {
380  SDL_free(thread->name);
381  }
382  SDL_free(thread);
383  thread = NULL;
384  }
385  SDL_DestroySemaphore(args->wait);
386  SDL_free(args);
387 
388  /* Everything is running now */
389  return (thread);
390 }
391 
392 #ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
394 SDL_CreateThread(int (SDLCALL * fn) (void *),
395  const char *name, void *data,
396  pfnSDL_CurrentBeginThread pfnBeginThread,
397  pfnSDL_CurrentEndThread pfnEndThread)
398 #else
400 SDL_CreateThread(int (SDLCALL * fn) (void *),
401  const char *name, void *data)
402 #endif
403 {
404  /* !!! FIXME: in 2.1, just make stackhint part of the usual API. */
405  const char *stackhint = SDL_GetHint(SDL_HINT_THREAD_STACK_SIZE);
406  size_t stacksize = 0;
407 
408  /* If the SDL_HINT_THREAD_STACK_SIZE exists, use it */
409  if (stackhint != NULL) {
410  char *endp = NULL;
411  const Sint64 hintval = SDL_strtoll(stackhint, &endp, 10);
412  if ((*stackhint != '\0') && (*endp == '\0')) { /* a valid number? */
413  if (hintval > 0) { /* reject bogus values. */
414  stacksize = (size_t) hintval;
415  }
416  }
417  }
418 
419 #ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
420  return SDL_CreateThreadWithStackSize(fn, name, stacksize, data, pfnBeginThread, pfnEndThread);
421 #else
422  return SDL_CreateThreadWithStackSize(fn, name, stacksize, data);
423 #endif
424 }
425 
426 SDL_Thread *
427 SDL_CreateThreadInternal(int (SDLCALL * fn) (void *), const char *name,
428  const size_t stacksize, void *data) {
429 #ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
430  return SDL_CreateThreadWithStackSize(fn, name, stacksize, data, NULL, NULL);
431 #else
432  return SDL_CreateThreadWithStackSize(fn, name, stacksize, data);
433 #endif
434 }
435 
438 {
440 
441  if (thread) {
442  id = thread->threadid;
443  } else {
444  id = SDL_ThreadID();
445  }
446  return id;
447 }
448 
449 const char *
451 {
452  if (thread) {
453  return thread->name;
454  } else {
455  return NULL;
456  }
457 }
458 
459 int
461 {
462  return SDL_SYS_SetThreadPriority(priority);
463 }
464 
465 void
467 {
468  if (thread) {
469  SDL_SYS_WaitThread(thread);
470  if (status) {
471  *status = thread->status;
472  }
473  if (thread->name) {
474  SDL_free(thread->name);
475  }
476  SDL_free(thread);
477  }
478 }
479 
480 void
482 {
483  if (!thread) {
484  return;
485  }
486 
487  /* Grab dibs if the state is alive+joinable. */
489  SDL_SYS_DetachThread(thread);
490  } else {
491  /* all other states are pretty final, see where we landed. */
492  const int thread_state = SDL_AtomicGet(&thread->state);
493  if ((thread_state == SDL_THREAD_STATE_DETACHED) || (thread_state == SDL_THREAD_STATE_CLEANED)) {
494  return; /* already detached (you shouldn't call this twice!) */
495  } else if (thread_state == SDL_THREAD_STATE_ZOMBIE) {
496  SDL_WaitThread(thread, NULL); /* already done, clean it up. */
497  } else {
498  SDL_assert(0 && "Unexpected thread state");
499  }
500  }
501 }
502 
503 /* vi: set ts=4 sw=4 expandtab: */
#define SDL_ThreadID
SDL_atomic_t state
Definition: SDL_thread_c.h:59
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
#define SDL_const_cast(type, expression)
Definition: SDL_stdinc.h:117
SDL_TLSData * storage
Definition: SDL_thread.c:115
#define SDL_LockMutex
GLuint id
static SDL_mutex * SDL_generic_TLS_mutex
Definition: SDL_thread.c:119
#define SDL_HINT_THREAD_STACK_SIZE
A string specifying SDL&#39;s threads stack size in bytes or "0" for the backend&#39;s default size...
Definition: SDL_hints.h:531
char * name
Definition: SDL_thread_c.h:61
struct SDL_TLSData::@33 array[1]
#define SDL_AtomicLock
#define SDL_MemoryBarrierRelease()
Definition: SDL_atomic.h:189
A type representing an atomic integer value. It is a struct so people don&#39;t accidentally use numeric ...
Definition: SDL_atomic.h:198
#define TLS_ALLOC_CHUNKSIZE
Definition: SDL_thread_c.h:79
void * data
Definition: SDL_thread_c.h:73
int SDL_SetThreadPriority(SDL_ThreadPriority priority)
Definition: SDL_thread.c:460
#define SDL_AtomicCAS
int SDL_Generic_SetTLSData(SDL_TLSData *storage)
Definition: SDL_thread.c:163
#define SDL_CreateSemaphore
#define SDL_CreateMutex
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_TLSData * SDL_Generic_GetTLSData(void)
Definition: SDL_thread.c:124
#define SDL_GetHint
unsigned int limit
Definition: SDL_thread_c.h:71
SDL_threadID thread
Definition: SDL_thread.c:114
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: SDL_opengl.h:1974
#define SDL_CreateThread
Definition: SDL_thread.c:304
int SDL_SYS_SetTLSData(SDL_TLSData *data)
Definition: SDL_systls.c:33
SDL_threadID threadid
Definition: SDL_thread_c.h:56
void SDL_DetachThread(SDL_Thread *thread)
Definition: SDL_thread.c:481
static SDL_mutex * mutex
Definition: testlock.c:23
#define SDL_InvalidParamError(param)
Definition: SDL_error.h:54
#define SDL_realloc
#define SDL_zerop(x)
Definition: SDL_stdinc.h:417
#define SDL_MemoryBarrierAcquire()
Definition: SDL_atomic.h:190
GLuint const GLchar * name
struct SDL_TLSEntry * next
Definition: SDL_thread.c:116
unsigned int SDL_TLSID
Definition: SDL_thread.h:52
#define SDL_SemPost
#define SDL_AtomicUnlock
void SDL_SYS_WaitThread(SDL_Thread *thread)
Definition: SDL_systhread.c:60
unsigned int size_t
SDL_TLSData * SDL_SYS_GetTLSData(void)
Definition: SDL_systls.c:27
#define DECLSPEC
Definition: SDL_internal.h:44
#define SDL_free
void(* destructor)(void *)
Definition: SDL_thread_c.h:74
int SDL_SYS_SetThreadPriority(SDL_ThreadPriority priority)
Definition: SDL_systhread.c:54
GLsizei const GLfloat * value
void SDL_SYS_DetachThread(SDL_Thread *thread)
Definition: SDL_systhread.c:66
static void SDL_TLSCleanup()
Definition: SDL_thread.c:87
SDL_error * SDL_GetErrBuf(void)
Definition: SDL_thread.c:206
#define SDL_AtomicIncRef(a)
Increment an atomic variable used as a reference count.
Definition: SDL_atomic.h:234
int(* func)(void *)
Definition: SDL_thread.c:258
SDL_threadID SDL_GetThreadID(SDL_Thread *thread)
Definition: SDL_thread.c:437
Definition: SDL_thread.c:113
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 SDL_assert(condition)
Definition: SDL_assert.h:169
#define NULL
Definition: begin_code.h:164
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
SDL_bool
Definition: SDL_stdinc.h:139
void SDL_WaitThread(SDL_Thread *thread, int *status)
Definition: SDL_thread.c:466
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
GLenum func
SDL_Thread * info
Definition: SDL_thread.c:260
static SDL_TLSEntry * SDL_generic_TLS
Definition: SDL_thread.c:120
SDL_ThreadPriority
Definition: SDL_thread.h:59
SDL_sem * wait
Definition: SDL_thread.c:261
#define SDL_strdup
#define SDL_SemWait
#define SDL_DestroySemaphore
void SDL_RunThread(void *data)
Definition: SDL_thread.c:265
void SDL_SYS_SetupThread(const char *name)
Definition: SDL_systhread.c:42
static SDL_Thread * SDL_CreateThreadWithStackSize(int(*fn)(void *), const char *name, const size_t stacksize, void *data)
Definition: SDL_thread.c:315
#define SDL_AtomicSet
#define SDL_AtomicGet
const char * SDL_GetThreadName(SDL_Thread *thread)
Definition: SDL_thread.c:450
#define SDL_UnlockMutex
#define SDL_malloc
#define SDL_strtoll
int64_t Sint64
Definition: SDL_stdinc.h:188
int SDL_SpinLock
Definition: SDL_atomic.h:89
void * data
Definition: SDL_thread.c:259
SDL_Thread * SDL_CreateThreadInternal(int(*fn)(void *), const char *name, const size_t stacksize, void *data)
Definition: SDL_thread.c:427
int SDL_SYS_CreateThread(SDL_Thread *thread, void *args)
Definition: SDL_systhread.c:35
#define SDLCALL
Definition: SDL_internal.h:45
size_t stacksize
Definition: SDL_thread_c.h:62
unsigned long SDL_threadID
Definition: SDL_thread.h:49