SDL  2.0
SDL_thread.c
Go to the documentation of this file.
1 /*
2  Simple DirectMedia Layer
3  Copyright (C) 1997-2016 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_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 (*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) {
136  SDL_generic_TLS_mutex = 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 
147  SDL_LockMutex(SDL_generic_TLS_mutex);
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
155  SDL_UnlockMutex(SDL_generic_TLS_mutex);
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 */
168  SDL_LockMutex(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  }
195  SDL_UnlockMutex(SDL_generic_TLS_mutex);
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  static SDL_SpinLock tls_lock;
208  static SDL_bool tls_being_created;
209  static SDL_TLSID tls_errbuf;
210  static SDL_error SDL_global_errbuf;
211  const SDL_error *ALLOCATION_IN_PROGRESS = (SDL_error *)-1;
212  SDL_error *errbuf;
213 
214  /* tls_being_created is there simply to prevent recursion if SDL_TLSCreate() fails.
215  It also means it's possible for another thread to also use SDL_global_errbuf,
216  but that's very unlikely and hopefully won't cause issues.
217  */
218  if (!tls_errbuf && !tls_being_created) {
219  SDL_AtomicLock(&tls_lock);
220  if (!tls_errbuf) {
221  SDL_TLSID slot;
222  tls_being_created = SDL_TRUE;
223  slot = SDL_TLSCreate();
224  tls_being_created = SDL_FALSE;
226  tls_errbuf = slot;
227  }
228  SDL_AtomicUnlock(&tls_lock);
229  }
230  if (!tls_errbuf) {
231  return &SDL_global_errbuf;
232  }
233 
235  errbuf = (SDL_error *)SDL_TLSGet(tls_errbuf);
236  if (errbuf == ALLOCATION_IN_PROGRESS) {
237  return &SDL_global_errbuf;
238  }
239  if (!errbuf) {
240  /* Mark that we're in the middle of allocating our buffer */
241  SDL_TLSSet(tls_errbuf, ALLOCATION_IN_PROGRESS, NULL);
242  errbuf = (SDL_error *)SDL_malloc(sizeof(*errbuf));
243  if (!errbuf) {
244  SDL_TLSSet(tls_errbuf, NULL, NULL);
245  return &SDL_global_errbuf;
246  }
247  SDL_zerop(errbuf);
248  SDL_TLSSet(tls_errbuf, errbuf, SDL_free);
249  }
250  return errbuf;
251 }
252 
253 
254 /* Arguments and callback to setup and run the user thread function */
255 typedef struct
256 {
257  int (SDLCALL * func) (void *);
258  void *data;
260  SDL_sem *wait;
261 } thread_args;
262 
263 void
265 {
266  thread_args *args = (thread_args *) data;
267  int (SDLCALL * userfunc) (void *) = args->func;
268  void *userdata = args->data;
269  SDL_Thread *thread = args->info;
270  int *statusloc = &thread->status;
271 
272  /* Perform any system-dependent setup - this function may not fail */
274 
275  /* Get the thread id */
276  thread->threadid = SDL_ThreadID();
277 
278  /* Wake up the parent thread */
279  SDL_SemPost(args->wait);
280 
281  /* Run the function */
282  *statusloc = userfunc(userdata);
283 
284  /* Clean up thread-local storage */
285  SDL_TLSCleanup();
286 
287  /* Mark us as ready to be joined (or detached) */
289  /* Clean up if something already detached us. */
291  if (thread->name) {
292  SDL_free(thread->name);
293  }
294  SDL_free(thread);
295  }
296  }
297 }
298 
299 #ifdef SDL_CreateThread
300 #undef SDL_CreateThread
301 #endif
302 #if SDL_DYNAMIC_API
303 #define SDL_CreateThread SDL_CreateThread_REAL
304 #endif
305 
306 #ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
308 SDL_CreateThread(int (SDLCALL * fn) (void *),
309  const char *name, void *data,
310  pfnSDL_CurrentBeginThread pfnBeginThread,
311  pfnSDL_CurrentEndThread pfnEndThread)
312 #else
314 SDL_CreateThread(int (SDLCALL * fn) (void *),
315  const char *name, void *data)
316 #endif
317 {
319  thread_args *args;
320  int ret;
321 
322  /* Allocate memory for the thread info structure */
323  thread = (SDL_Thread *) SDL_malloc(sizeof(*thread));
324  if (thread == NULL) {
325  SDL_OutOfMemory();
326  return (NULL);
327  }
328  SDL_zerop(thread);
329  thread->status = -1;
331 
332  /* Set up the arguments for the thread */
333  if (name != NULL) {
334  thread->name = SDL_strdup(name);
335  if (thread->name == NULL) {
336  SDL_OutOfMemory();
337  SDL_free(thread);
338  return (NULL);
339  }
340  }
341 
342  /* Set up the arguments for the thread */
343  args = (thread_args *) SDL_malloc(sizeof(*args));
344  if (args == NULL) {
345  SDL_OutOfMemory();
346  if (thread->name) {
347  SDL_free(thread->name);
348  }
349  SDL_free(thread);
350  return (NULL);
351  }
352  args->func = fn;
353  args->data = data;
354  args->info = thread;
355  args->wait = SDL_CreateSemaphore(0);
356  if (args->wait == NULL) {
357  if (thread->name) {
358  SDL_free(thread->name);
359  }
360  SDL_free(thread);
361  SDL_free(args);
362  return (NULL);
363  }
364 
365  /* Create the thread and go! */
366 #ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
367  ret = SDL_SYS_CreateThread(thread, args, pfnBeginThread, pfnEndThread);
368 #else
369  ret = SDL_SYS_CreateThread(thread, args);
370 #endif
371  if (ret >= 0) {
372  /* Wait for the thread function to use arguments */
373  SDL_SemWait(args->wait);
374  } else {
375  /* Oops, failed. Gotta free everything */
376  if (thread->name) {
377  SDL_free(thread->name);
378  }
379  SDL_free(thread);
380  thread = NULL;
381  }
382  SDL_DestroySemaphore(args->wait);
383  SDL_free(args);
384 
385  /* Everything is running now */
386  return (thread);
387 }
388 
391 {
393 
394  if (thread) {
395  id = thread->threadid;
396  } else {
397  id = SDL_ThreadID();
398  }
399  return id;
400 }
401 
402 const char *
404 {
405  if (thread) {
406  return thread->name;
407  } else {
408  return NULL;
409  }
410 }
411 
412 int
414 {
415  return SDL_SYS_SetThreadPriority(priority);
416 }
417 
418 void
420 {
421  if (thread) {
422  SDL_SYS_WaitThread(thread);
423  if (status) {
424  *status = thread->status;
425  }
426  if (thread->name) {
427  SDL_free(thread->name);
428  }
429  SDL_free(thread);
430  }
431 }
432 
433 void
435 {
436  if (!thread) {
437  return;
438  }
439 
440  /* Grab dibs if the state is alive+joinable. */
442  SDL_SYS_DetachThread(thread);
443  } else {
444  /* all other states are pretty final, see where we landed. */
445  const int thread_state = SDL_AtomicGet(&thread->state);
446  if ((thread_state == SDL_THREAD_STATE_DETACHED) || (thread_state == SDL_THREAD_STATE_CLEANED)) {
447  return; /* already detached (you shouldn't call this twice!) */
448  } else if (thread_state == SDL_THREAD_STATE_ZOMBIE) {
449  SDL_WaitThread(thread, NULL); /* already done, clean it up. */
450  } else {
451  SDL_assert(0 && "Unexpected thread state");
452  }
453  }
454 }
455 
456 /* 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:52
#define SDL_const_cast(type, expression)
Definition: SDL_stdinc.h:110
SDL_TLSData * storage
Definition: SDL_thread.c:114
#define SDL_LockMutex
GLuint id
static SDL_mutex * SDL_generic_TLS_mutex
Definition: SDL_thread.c:118
char * name
Definition: SDL_thread_c.h:61
#define SDL_AtomicLock
SDL_TLSData * SDL_Generic_GetTLSData()
Definition: SDL_thread.c:123
#define SDL_MemoryBarrierRelease()
Definition: SDL_atomic.h:180
A type representing an atomic integer value. It is a struct so people don&#39;t accidentally use numeric ...
Definition: SDL_atomic.h:189
#define TLS_ALLOC_CHUNKSIZE
Definition: SDL_thread_c.h:78
void * data
Definition: SDL_thread_c.h:72
int SDL_SetThreadPriority(SDL_ThreadPriority priority)
Definition: SDL_thread.c:413
#define SDL_AtomicCAS
int SDL_Generic_SetTLSData(SDL_TLSData *storage)
Definition: SDL_thread.c:162
#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:40
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: SDL_opengl.h:1967
unsigned int limit
Definition: SDL_thread_c.h:70
SDL_threadID thread
Definition: SDL_thread.c:113
#define SDL_CreateThread
Definition: SDL_thread.c:303
int SDL_SYS_SetTLSData(SDL_TLSData *data)
Definition: SDL_systls.c:33
GLuint const GLchar * name
SDL_threadID threadid
Definition: SDL_thread_c.h:56
void SDL_DetachThread(SDL_Thread *thread)
Definition: SDL_thread.c:434
static SDL_mutex * mutex
Definition: testlock.c:23
#define SDL_InvalidParamError(param)
Definition: SDL_error.h:54
#define SDL_realloc
SDL_TLSData * SDL_SYS_GetTLSData()
Definition: SDL_systls.c:27
#define SDL_zerop(x)
Definition: SDL_stdinc.h:356
#define SDL_MemoryBarrierAcquire()
Definition: SDL_atomic.h:181
struct SDL_TLSEntry * next
Definition: SDL_thread.c:115
unsigned int SDL_TLSID
Definition: SDL_thread.h:52
#define SDL_SemPost
#define SDL_AtomicUnlock
SDL_bool
Definition: SDL_stdinc.h:126
void SDL_SYS_WaitThread(SDL_Thread *thread)
Definition: SDL_systhread.c:60
#define DECLSPEC
Definition: SDL_internal.h:30
GLsizei const GLfloat * value
void SDL_free(void *mem)
int SDL_SYS_SetThreadPriority(SDL_ThreadPriority priority)
Definition: SDL_systhread.c:54
void SDL_SYS_DetachThread(SDL_Thread *thread)
Definition: SDL_systhread.c:66
static void SDL_TLSCleanup()
Definition: SDL_thread.c:86
SDL_error * SDL_GetErrBuf(void)
Definition: SDL_thread.c:205
#define SDL_AtomicIncRef(a)
Increment an atomic variable used as a reference count.
Definition: SDL_atomic.h:225
int(* func)(void *)
Definition: SDL_thread.c:257
SDL_threadID SDL_GetThreadID(SDL_Thread *thread)
Definition: SDL_thread.c:390
Definition: SDL_thread.c:112
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:42
#define SDL_assert(condition)
Definition: SDL_assert.h:167
#define NULL
Definition: begin_code.h:143
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
void SDL_WaitThread(SDL_Thread *thread, int *status)
Definition: SDL_thread.c:419
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
GLenum func
SDL_Thread * info
Definition: SDL_thread.c:259
static SDL_TLSEntry * SDL_generic_TLS
Definition: SDL_thread.c:119
SDL_ThreadPriority
Definition: SDL_thread.h:59
SDL_sem * wait
Definition: SDL_thread.c:260
#define SDL_strdup
#define SDL_SemWait
#define SDL_DestroySemaphore
void SDL_RunThread(void *data)
Definition: SDL_thread.c:264
void SDL_SYS_SetupThread(const char *name)
Definition: SDL_systhread.c:42
#define SDL_AtomicSet
#define SDL_AtomicGet
const char * SDL_GetThreadName(SDL_Thread *thread)
Definition: SDL_thread.c:403
#define SDL_UnlockMutex
#define SDL_malloc
void(* destructor)(void *)
Definition: SDL_thread_c.h:73
int SDL_SpinLock
Definition: SDL_atomic.h:89
void * data
Definition: SDL_thread.c:258
int SDL_SYS_CreateThread(SDL_Thread *thread, void *args)
Definition: SDL_systhread.c:35
#define SDLCALL
Definition: SDL_internal.h:31
struct SDL_TLSData::@25 array[1]
unsigned long SDL_threadID
Definition: SDL_thread.h:49