SDL  2.0
SDL_thread.c File Reference
#include "../SDL_internal.h"
#include "SDL_assert.h"
#include "SDL_thread.h"
#include "SDL_thread_c.h"
#include "SDL_systhread.h"
#include "SDL_hints.h"
#include "../SDL_error_c.h"
+ Include dependency graph for SDL_thread.c:

Go to the source code of this file.

Data Structures

struct  SDL_TLSEntry
struct  thread_args

Macros

#define SDL_CreateThread   SDL_CreateThread_REAL
#define SDL_CreateThreadWithStackSize   SDL_CreateThreadWithStackSize_REAL

Functions

SDL_TLSID SDL_TLSCreate ()
 Create an identifier that is globally visible to all threads but refers to data that is thread-specific.
voidSDL_TLSGet (SDL_TLSID id)
 Get the value associated with a thread local storage ID for the current thread.
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.
static void SDL_TLSCleanup ()
SDL_TLSDataSDL_Generic_GetTLSData (void)
int SDL_Generic_SetTLSData (SDL_TLSData *storage)
SDL_errorSDL_GetErrBuf (void)
void SDL_RunThread (void *data)
SDL_ThreadSDL_CreateThreadWithStackSize (int(*fn)(void *), const char *name, const size_t stacksize, void *data)
SDL_ThreadSDL_CreateThread (int(*fn)(void *), const char *name, void *data)
SDL_ThreadSDL_CreateThreadInternal (int(*fn)(void *), const char *name, const size_t stacksize, void *data)
SDL_threadID SDL_GetThreadID (SDL_Thread *thread)
const char * SDL_GetThreadName (SDL_Thread *thread)
int SDL_SetThreadPriority (SDL_ThreadPriority priority)
void SDL_WaitThread (SDL_Thread *thread, int *status)
void SDL_DetachThread (SDL_Thread *thread)

Variables

static SDL_mutexSDL_generic_TLS_mutex
static SDL_TLSEntrySDL_generic_TLS

Macro Definition Documentation

#define SDL_CreateThread   SDL_CreateThread_REAL

Definition at line 305 of file SDL_thread.c.

#define SDL_CreateThreadWithStackSize   SDL_CreateThreadWithStackSize_REAL

Definition at line 306 of file SDL_thread.c.

Function Documentation

SDL_Thread* SDL_CreateThread ( int(*)(void *)  fn,
const char *  name,
void data 
)

Definition at line 402 of file SDL_thread.c.

References NULL, SDL_CreateThreadWithStackSize, SDL_GetHint, SDL_HINT_THREAD_STACK_SIZE, and SDL_strtoll.

{
/* !!! FIXME: in 2.1, just make stackhint part of the usual API. */
const char *stackhint = SDL_GetHint(SDL_HINT_THREAD_STACK_SIZE);
size_t stacksize = 0;
/* If the SDL_HINT_THREAD_STACK_SIZE exists, use it */
if (stackhint != NULL) {
char *endp = NULL;
const Sint64 hintval = SDL_strtoll(stackhint, &endp, 10);
if ((*stackhint != '\0') && (*endp == '\0')) { /* a valid number? */
if (hintval > 0) { /* reject bogus values. */
stacksize = (size_t) hintval;
}
}
}
#ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
return SDL_CreateThreadWithStackSize(fn, name, stacksize, data, pfnBeginThread, pfnEndThread);
#else
return SDL_CreateThreadWithStackSize(fn, name, stacksize, data);
#endif
}
SDL_Thread* SDL_CreateThreadInternal ( int(*)(void *)  fn,
const char *  name,
const size_t  stacksize,
void data 
)

Definition at line 429 of file SDL_thread.c.

References NULL, and SDL_CreateThreadWithStackSize.

Referenced by open_audio_device(), and SDL_TimerInit().

{
#ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
return SDL_CreateThreadWithStackSize(fn, name, stacksize, data, NULL, NULL);
#else
return SDL_CreateThreadWithStackSize(fn, name, stacksize, data);
#endif
}
SDL_Thread* SDL_CreateThreadWithStackSize ( int(*)(void *)  fn,
const char *  name,
const size_t  stacksize,
void data 
)

Definition at line 317 of file SDL_thread.c.

References thread_args::data, thread_args::func, thread_args::info, SDL_Thread::name, NULL, SDL_AtomicSet, SDL_CreateSemaphore, SDL_DestroySemaphore, SDL_free, SDL_malloc, SDL_OutOfMemory, SDL_SemWait, SDL_strdup, SDL_SYS_CreateThread(), SDL_THREAD_STATE_ALIVE, SDL_zerop, SDL_Thread::stacksize, SDL_Thread::state, SDL_Thread::status, SDL_TLSEntry::thread, and thread_args::wait.

{
SDL_Thread *thread;
thread_args *args;
int ret;
/* Allocate memory for the thread info structure */
thread = (SDL_Thread *) SDL_malloc(sizeof(*thread));
if (thread == NULL) {
return (NULL);
}
SDL_zerop(thread);
thread->status = -1;
/* Set up the arguments for the thread */
if (name != NULL) {
thread->name = SDL_strdup(name);
if (thread->name == NULL) {
SDL_free(thread);
return (NULL);
}
}
/* Set up the arguments for the thread */
args = (thread_args *) SDL_malloc(sizeof(*args));
if (args == NULL) {
if (thread->name) {
SDL_free(thread->name);
}
SDL_free(thread);
return (NULL);
}
args->func = fn;
args->data = data;
args->info = thread;
if (args->wait == NULL) {
if (thread->name) {
SDL_free(thread->name);
}
SDL_free(thread);
SDL_free(args);
return (NULL);
}
thread->stacksize = stacksize;
/* Create the thread and go! */
#ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
ret = SDL_SYS_CreateThread(thread, args, pfnBeginThread, pfnEndThread);
#else
ret = SDL_SYS_CreateThread(thread, args);
#endif
if (ret >= 0) {
/* Wait for the thread function to use arguments */
SDL_SemWait(args->wait);
} else {
/* Oops, failed. Gotta free everything */
if (thread->name) {
SDL_free(thread->name);
}
SDL_free(thread);
thread = NULL;
}
SDL_free(args);
/* Everything is running now */
return (thread);
}
void SDL_DetachThread ( SDL_Thread thread)

A thread may be "detached" to signify that it should not remain until another thread has called SDL_WaitThread() on it. Detaching a thread is useful for long-running threads that nothing needs to synchronize with or further manage. When a detached thread is done, it simply goes away.

There is no way to recover the return code of a detached thread. If you need this, don't detach the thread and instead use SDL_WaitThread().

Once a thread is detached, you should usually assume the SDL_Thread isn't safe to reference again, as it will become invalid immediately upon the detached thread's exit, instead of remaining until someone has called SDL_WaitThread() to finally clean it up. As such, don't detach the same thread more than once.

If a thread has already exited when passed to SDL_DetachThread(), it will stop waiting for a call to SDL_WaitThread() and clean up immediately. It is not safe to detach a thread that might be used with SDL_WaitThread().

You may not call SDL_WaitThread() on a thread that has been detached. Use either that function or this one, but not both, or behavior is undefined.

It is safe to pass NULL to this function; it is a no-op.

Definition at line 483 of file SDL_thread.c.

References NULL, SDL_assert, SDL_AtomicCAS, SDL_AtomicGet, SDL_SYS_DetachThread(), SDL_THREAD_STATE_ALIVE, SDL_THREAD_STATE_CLEANED, SDL_THREAD_STATE_DETACHED, SDL_THREAD_STATE_ZOMBIE, SDL_WaitThread, and SDL_Thread::state.

{
if (!thread) {
return;
}
/* Grab dibs if the state is alive+joinable. */
} else {
/* all other states are pretty final, see where we landed. */
const int thread_state = SDL_AtomicGet(&thread->state);
if ((thread_state == SDL_THREAD_STATE_DETACHED) || (thread_state == SDL_THREAD_STATE_CLEANED)) {
return; /* already detached (you shouldn't call this twice!) */
} else if (thread_state == SDL_THREAD_STATE_ZOMBIE) {
SDL_WaitThread(thread, NULL); /* already done, clean it up. */
} else {
SDL_assert(0 && "Unexpected thread state");
}
}
}
SDL_TLSData* SDL_Generic_GetTLSData ( void  )

Definition at line 124 of file SDL_thread.c.

References mutex, SDL_TLSEntry::next, NULL, SDL_AtomicLock, SDL_AtomicUnlock, SDL_CreateMutex, SDL_LockMutex, SDL_MemoryBarrierAcquire, SDL_MemoryBarrierRelease, SDL_ThreadID, SDL_UnlockMutex, SDL_TLSEntry::storage, and SDL_TLSEntry::thread.

Referenced by SDL_SYS_GetTLSData().

{
SDL_TLSEntry *entry;
SDL_TLSData *storage = NULL;
#if !SDL_THREADS_DISABLED
static SDL_SpinLock tls_lock;
SDL_AtomicLock(&tls_lock);
SDL_AtomicUnlock(&tls_lock);
return NULL;
}
}
SDL_AtomicUnlock(&tls_lock);
}
#endif /* SDL_THREADS_DISABLED */
for (entry = SDL_generic_TLS; entry; entry = entry->next) {
if (entry->thread == thread) {
storage = entry->storage;
break;
}
}
#if !SDL_THREADS_DISABLED
#endif
return storage;
}
int SDL_Generic_SetTLSData ( SDL_TLSData storage)

Definition at line 163 of file SDL_thread.c.

References SDL_TLSEntry::next, NULL, SDL_free, SDL_generic_TLS, SDL_LockMutex, SDL_malloc, SDL_OutOfMemory, SDL_ThreadID, SDL_UnlockMutex, SDL_TLSEntry::storage, and SDL_TLSEntry::thread.

Referenced by SDL_SYS_SetTLSData().

{
SDL_TLSEntry *prev, *entry;
/* SDL_Generic_GetTLSData() is always called first, so we can assume SDL_generic_TLS_mutex */
prev = NULL;
for (entry = SDL_generic_TLS; entry; entry = entry->next) {
if (entry->thread == thread) {
if (storage) {
entry->storage = storage;
} else {
if (prev) {
prev->next = entry->next;
} else {
}
SDL_free(entry);
}
break;
}
prev = entry;
}
if (!entry) {
entry = (SDL_TLSEntry *)SDL_malloc(sizeof(*entry));
if (entry) {
entry->thread = thread;
entry->storage = storage;
SDL_generic_TLS = entry;
}
}
if (!entry) {
return SDL_OutOfMemory();
}
return 0;
}
SDL_error* SDL_GetErrBuf ( void  )

Definition at line 206 of file SDL_thread.c.

References NULL, SDL_AtomicLock, SDL_AtomicUnlock, SDL_FALSE, SDL_free, SDL_malloc, SDL_MemoryBarrierAcquire, SDL_MemoryBarrierRelease, SDL_TLSCreate, SDL_TLSGet, SDL_TLSSet, SDL_TRUE, and SDL_zerop.

Referenced by SDL_ClearError(), SDL_GetErrorMsg(), and SDL_SetError().

{
static SDL_SpinLock tls_lock;
static SDL_bool tls_being_created;
static SDL_TLSID tls_errbuf;
static SDL_error SDL_global_errbuf;
const SDL_error *ALLOCATION_IN_PROGRESS = (SDL_error *)-1;
SDL_error *errbuf;
/* tls_being_created is there simply to prevent recursion if SDL_TLSCreate() fails.
It also means it's possible for another thread to also use SDL_global_errbuf,
but that's very unlikely and hopefully won't cause issues.
*/
if (!tls_errbuf && !tls_being_created) {
SDL_AtomicLock(&tls_lock);
if (!tls_errbuf) {
SDL_TLSID slot;
tls_being_created = SDL_TRUE;
slot = SDL_TLSCreate();
tls_being_created = SDL_FALSE;
tls_errbuf = slot;
}
SDL_AtomicUnlock(&tls_lock);
}
if (!tls_errbuf) {
return &SDL_global_errbuf;
}
errbuf = (SDL_error *)SDL_TLSGet(tls_errbuf);
if (errbuf == ALLOCATION_IN_PROGRESS) {
return &SDL_global_errbuf;
}
if (!errbuf) {
/* Mark that we're in the middle of allocating our buffer */
SDL_TLSSet(tls_errbuf, ALLOCATION_IN_PROGRESS, NULL);
errbuf = (SDL_error *)SDL_malloc(sizeof(*errbuf));
if (!errbuf) {
SDL_TLSSet(tls_errbuf, NULL, NULL);
return &SDL_global_errbuf;
}
SDL_zerop(errbuf);
SDL_TLSSet(tls_errbuf, errbuf, SDL_free);
}
return errbuf;
}
SDL_threadID SDL_GetThreadID ( SDL_Thread thread)

Get the thread identifier for the specified thread.

Equivalent to SDL_ThreadID() if the specified thread is NULL.

Definition at line 439 of file SDL_thread.c.

References SDL_ThreadID, and SDL_Thread::threadid.

{
if (thread) {
id = thread->threadid;
} else {
id = SDL_ThreadID();
}
return id;
}
const char* SDL_GetThreadName ( SDL_Thread thread)

Get the thread name, as it was specified in SDL_CreateThread(). This function returns a pointer to a UTF-8 string that names the specified thread, or NULL if it doesn't have a name. This is internal memory, not to be free()'d by the caller, and remains valid until the specified thread is cleaned up by SDL_WaitThread().

Definition at line 452 of file SDL_thread.c.

References SDL_Thread::name, and NULL.

{
if (thread) {
return thread->name;
} else {
return NULL;
}
}
void SDL_RunThread ( void data)

Definition at line 265 of file SDL_thread.c.

References thread_args::data, thread_args::func, thread_args::info, SDL_AtomicCAS, SDL_free, SDL_SemPost, SDL_SYS_SetupThread(), SDL_THREAD_STATE_ALIVE, SDL_THREAD_STATE_CLEANED, SDL_THREAD_STATE_DETACHED, SDL_THREAD_STATE_ZOMBIE, SDL_ThreadID, SDL_TLSCleanup(), SDLCALL, SDL_TLSEntry::thread, and thread_args::wait.

Referenced by RunThread().

{
int (SDLCALL * userfunc) (void *) = args->func;
void *userdata = args->data;
SDL_Thread *thread = args->info;
int *statusloc = &thread->status;
/* Perform any system-dependent setup - this function may not fail */
SDL_SYS_SetupThread(thread->name);
/* Get the thread id */
thread->threadid = SDL_ThreadID();
/* Wake up the parent thread */
SDL_SemPost(args->wait);
/* Run the function */
*statusloc = userfunc(userdata);
/* Clean up thread-local storage */
/* Mark us as ready to be joined (or detached) */
/* Clean up if something already detached us. */
if (thread->name) {
SDL_free(thread->name);
}
SDL_free(thread);
}
}
}
int SDL_SetThreadPriority ( SDL_ThreadPriority  priority)

Set the priority for the current thread

Definition at line 462 of file SDL_thread.c.

References SDL_SYS_SetThreadPriority().

{
return SDL_SYS_SetThreadPriority(priority);
}
static void SDL_TLSCleanup ( )
static

Definition at line 87 of file SDL_thread.c.

References SDL_TLSData::array, SDL_TLSData::data, SDL_TLSData::destructor, i, SDL_TLSData::limit, NULL, SDL_free, SDL_SYS_GetTLSData(), and SDL_SYS_SetTLSData().

Referenced by SDL_RunThread().

{
SDL_TLSData *storage;
storage = SDL_SYS_GetTLSData();
if (storage) {
unsigned int i;
for (i = 0; i < storage->limit; ++i) {
if (storage->array[i].destructor) {
storage->array[i].destructor(storage->array[i].data);
}
}
SDL_free(storage);
}
}
SDL_TLSID SDL_TLSCreate ( void  )

Create an identifier that is globally visible to all threads but refers to data that is thread-specific.

Returns
The newly created thread local storage identifier, or 0 on error
static SDL_SpinLock tls_lock;
void SetMyThreadData(void *value)
{
if (!thread_local_storage) {
SDL_AtomicLock(&tls_lock);
if (!thread_local_storage) {
thread_local_storage = SDL_TLSCreate();
}
SDL_AtomicUnlock(&tls_lock);
}
SDL_TLSSet(thread_local_storage, value, 0);
}
void *GetMyThreadData(void)
{
return SDL_TLSGet(thread_local_storage);
}
See Also
SDL_TLSGet()
SDL_TLSSet()

Definition at line 34 of file SDL_thread.c.

References SDL_AtomicIncRef.

{
static SDL_atomic_t SDL_tls_id;
return SDL_AtomicIncRef(&SDL_tls_id)+1;
}
void* SDL_TLSGet ( SDL_TLSID  id)

Get the value associated with a thread local storage ID for the current thread.

Parameters
idThe thread local storage ID
Returns
The value associated with the ID for the current thread, or NULL if no value has been set.
See Also
SDL_TLSCreate()
SDL_TLSSet()

Definition at line 41 of file SDL_thread.c.

References SDL_TLSData::array, SDL_TLSData::data, SDL_TLSData::limit, NULL, and SDL_SYS_GetTLSData().

{
SDL_TLSData *storage;
storage = SDL_SYS_GetTLSData();
if (!storage || id == 0 || id > storage->limit) {
return NULL;
}
return storage->array[id-1].data;
}
int SDL_TLSSet ( SDL_TLSID  id,
const void value,
void(*)(void *)  destructor 
)

Set the value associated with a thread local storage ID for the current thread.

Parameters
idThe thread local storage ID
valueThe value to associate with the ID for the current thread
destructorA function called when the thread exits, to free the value.
Returns
0 on success, -1 on error
See Also
SDL_TLSCreate()
SDL_TLSGet()

Definition at line 53 of file SDL_thread.c.

References SDL_TLSData::array, SDL_TLSData::data, SDL_TLSData::destructor, i, SDL_TLSData::limit, NULL, SDL_const_cast, SDL_InvalidParamError, SDL_OutOfMemory, SDL_realloc, SDL_SYS_GetTLSData(), SDL_SYS_SetTLSData(), and TLS_ALLOC_CHUNKSIZE.

{
SDL_TLSData *storage;
if (id == 0) {
return SDL_InvalidParamError("id");
}
storage = SDL_SYS_GetTLSData();
if (!storage || (id > storage->limit)) {
unsigned int i, oldlimit, newlimit;
oldlimit = storage ? storage->limit : 0;
newlimit = (id + TLS_ALLOC_CHUNKSIZE);
storage = (SDL_TLSData *)SDL_realloc(storage, sizeof(*storage)+(newlimit-1)*sizeof(storage->array[0]));
if (!storage) {
return SDL_OutOfMemory();
}
storage->limit = newlimit;
for (i = oldlimit; i < newlimit; ++i) {
storage->array[i].data = NULL;
storage->array[i].destructor = NULL;
}
if (SDL_SYS_SetTLSData(storage) != 0) {
return -1;
}
}
storage->array[id-1].data = SDL_const_cast(void*, value);
storage->array[id-1].destructor = destructor;
return 0;
}
void SDL_WaitThread ( SDL_Thread thread,
int *  status 
)

Wait for a thread to finish. Threads that haven't been detached will remain (as a "zombie") until this function cleans them up. Not doing so is a resource leak.

Once a thread has been cleaned up through this function, the SDL_Thread that references it becomes invalid and should not be referenced again. As such, only one thread may call SDL_WaitThread() on another.

The return code for the thread function is placed in the area pointed to by status, if status is not NULL.

You may not wait on a thread that has been used in a call to SDL_DetachThread(). Use either that function or this one, but not both, or behavior is undefined.

It is safe to pass NULL to this function; it is a no-op.

Definition at line 468 of file SDL_thread.c.

References SDL_Thread::name, SDL_free, SDL_SYS_WaitThread(), and SDL_Thread::status.

{
if (thread) {
if (status) {
*status = thread->status;
}
if (thread->name) {
SDL_free(thread->name);
}
SDL_free(thread);
}
}

Variable Documentation

SDL_TLSEntry* SDL_generic_TLS
static

Definition at line 120 of file SDL_thread.c.

Referenced by SDL_Generic_SetTLSData().

SDL_mutex* SDL_generic_TLS_mutex
static

Definition at line 119 of file SDL_thread.c.