SDL_thread.c
Go to the documentation of this file.
/*
Simple DirectMedia Layer
Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "../SDL_internal.h"
/* System independent thread management routines for SDL */
#include "SDL_assert.h"
#include "SDL_thread.h"
#include "SDL_thread_c.h"
#include "SDL_systhread.h"
#include "../SDL_error_c.h"
SDL_TLSID
SDL_TLSCreate()
{
static SDL_atomic_t SDL_tls_id;
return SDL_AtomicIncRef(&SDL_tls_id)+1;
}
void *
SDL_TLSGet(SDL_TLSID id)
{
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 (*destructor)(void *))
{
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;
}
static void
SDL_TLSCleanup()
{
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_SYS_SetTLSData(NULL);
SDL_free(storage);
}
}
/* This is a generic implementation of thread-local storage which doesn't
require additional OS support.
It is not especially efficient and doesn't clean up thread-local storage
as threads exit. If there is a real OS that doesn't support thread-local
storage this implementation should be improved to be production quality.
*/
typedef struct SDL_TLSEntry {
SDL_threadID thread;
SDL_TLSData *storage;
struct SDL_TLSEntry *next;
} SDL_TLSEntry;
static SDL_mutex *SDL_generic_TLS_mutex;
static SDL_TLSEntry *SDL_generic_TLS;
SDL_TLSData *
SDL_Generic_GetTLSData()
{
SDL_threadID thread = SDL_ThreadID();
SDL_TLSEntry *entry;
SDL_TLSData *storage = NULL;
#if !SDL_THREADS_DISABLED
if (!SDL_generic_TLS_mutex) {
static SDL_SpinLock tls_lock;
SDL_AtomicLock(&tls_lock);
if (!SDL_generic_TLS_mutex) {
SDL_mutex *mutex = SDL_CreateMutex();
SDL_MemoryBarrierRelease();
SDL_generic_TLS_mutex = mutex;
if (!SDL_generic_TLS_mutex) {
SDL_AtomicUnlock(&tls_lock);
return NULL;
}
}
SDL_AtomicUnlock(&tls_lock);
}
#endif /* SDL_THREADS_DISABLED */
SDL_MemoryBarrierAcquire();
SDL_LockMutex(SDL_generic_TLS_mutex);
for (entry = SDL_generic_TLS; entry; entry = entry->next) {
if (entry->thread == thread) {
storage = entry->storage;
break;
}
}
#if !SDL_THREADS_DISABLED
SDL_UnlockMutex(SDL_generic_TLS_mutex);
#endif
return storage;
}
int
SDL_Generic_SetTLSData(SDL_TLSData *storage)
{
SDL_threadID thread = SDL_ThreadID();
SDL_TLSEntry *prev, *entry;
/* SDL_Generic_GetTLSData() is always called first, so we can assume SDL_generic_TLS_mutex */
SDL_LockMutex(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_generic_TLS = entry->next;
}
SDL_free(entry);
}
break;
}
prev = entry;
}
if (!entry) {
entry = (SDL_TLSEntry *)SDL_malloc(sizeof(*entry));
if (entry) {
entry->thread = thread;
entry->storage = storage;
entry->next = SDL_generic_TLS;
SDL_generic_TLS = entry;
}
}
SDL_UnlockMutex(SDL_generic_TLS_mutex);
if (!entry) {
return SDL_OutOfMemory();
}
return 0;
}
/* Routine to get the thread-specific error variable */
SDL_error *
SDL_GetErrBuf(void)
{
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;
SDL_MemoryBarrierRelease();
tls_errbuf = slot;
}
SDL_AtomicUnlock(&tls_lock);
}
if (!tls_errbuf) {
return &SDL_global_errbuf;
}
SDL_MemoryBarrierAcquire();
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;
}
/* Arguments and callback to setup and run the user thread function */
typedef struct
{
int (SDLCALL * func) (void *);
void *data;
SDL_Thread *info;
SDL_sem *wait;
} thread_args;
void
SDL_RunThread(void *data)
{
thread_args *args = (thread_args *) data;
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 */
SDL_TLSCleanup();
/* Mark us as ready to be joined (or detached) */
if (!SDL_AtomicCAS(&thread->state, SDL_THREAD_STATE_ALIVE, SDL_THREAD_STATE_ZOMBIE)) {
/* Clean up if something already detached us. */
if (SDL_AtomicCAS(&thread->state, SDL_THREAD_STATE_DETACHED, SDL_THREAD_STATE_CLEANED)) {
if (thread->name) {
SDL_free(thread->name);
}
SDL_free(thread);
}
}
}
#ifdef SDL_CreateThread
#undef SDL_CreateThread
#endif
#if SDL_DYNAMIC_API
#define SDL_CreateThread SDL_CreateThread_REAL
#endif
#ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
DECLSPEC SDL_Thread *SDLCALL
SDL_CreateThread(int (SDLCALL * fn) (void *),
const char *name, void *data,
pfnSDL_CurrentBeginThread pfnBeginThread,
pfnSDL_CurrentEndThread pfnEndThread)
#else
DECLSPEC SDL_Thread *SDLCALL
SDL_CreateThread(int (SDLCALL * fn) (void *),
const char *name, void *data)
#endif
{
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) {
SDL_OutOfMemory();
return (NULL);
}
SDL_zerop(thread);
thread->status = -1;
SDL_AtomicSet(&thread->state, SDL_THREAD_STATE_ALIVE);
/* Set up the arguments for the thread */
if (name != NULL) {
thread->name = SDL_strdup(name);
if (thread->name == NULL) {
SDL_OutOfMemory();
SDL_free(thread);
return (NULL);
}
}
/* Set up the arguments for the thread */
args = (thread_args *) SDL_malloc(sizeof(*args));
if (args == NULL) {
SDL_OutOfMemory();
if (thread->name) {
SDL_free(thread->name);
}
SDL_free(thread);
return (NULL);
}
args->func = fn;
args->data = data;
args->info = thread;
args->wait = SDL_CreateSemaphore(0);
if (args->wait == NULL) {
if (thread->name) {
SDL_free(thread->name);
}
SDL_free(thread);
SDL_free(args);
return (NULL);
}
/* 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_DestroySemaphore(args->wait);
SDL_free(args);
/* Everything is running now */
return (thread);
}
SDL_threadID
SDL_GetThreadID(SDL_Thread * thread)
{
SDL_threadID id;
if (thread) {
id = thread->threadid;
} else {
id = SDL_ThreadID();
}
return id;
}
const char *
SDL_GetThreadName(SDL_Thread * thread)
{
if (thread) {
return thread->name;
} else {
return NULL;
}
}
int
SDL_SetThreadPriority(SDL_ThreadPriority priority)
{
return SDL_SYS_SetThreadPriority(priority);
}
void
SDL_WaitThread(SDL_Thread * thread, int *status)
{
if (thread) {
SDL_SYS_WaitThread(thread);
if (status) {
*status = thread->status;
}
if (thread->name) {
SDL_free(thread->name);
}
SDL_free(thread);
}
}
void
SDL_DetachThread(SDL_Thread * thread)
{
if (!thread) {
return;
}
/* Grab dibs if the state is alive+joinable. */
if (SDL_AtomicCAS(&thread->state, SDL_THREAD_STATE_ALIVE, SDL_THREAD_STATE_DETACHED)) {
SDL_SYS_DetachThread(thread);
} 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");
}
}
}
/* vi: set ts=4 sw=4 expandtab: */
SDL_ThreadID
#define SDL_ThreadID
Definition: SDL_dynapi_overrides.h:479
SDL_Thread::state
SDL_atomic_t state
Definition: SDL_thread_c.h:59
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:52
SDL_const_cast
#define SDL_const_cast(type, expression)
Definition: SDL_stdinc.h:110
SDL_TLSEntry::storage
SDL_TLSData * storage
Definition: SDL_thread.c:114
SDL_LockMutex
#define SDL_LockMutex
Definition: SDL_dynapi_overrides.h:266
id
GLuint id
Definition: SDL_opengl_glext.h:528
SDL_generic_TLS_mutex
static SDL_mutex * SDL_generic_TLS_mutex
Definition: SDL_thread.c:118
SDL_THREAD_STATE_ZOMBIE
Definition: SDL_thread_c.h:49
SDL_Thread::name
char * name
Definition: SDL_thread_c.h:61
SDL_AtomicLock
#define SDL_AtomicLock
Definition: SDL_dynapi_overrides.h:70
SDL_Generic_GetTLSData
SDL_TLSData * SDL_Generic_GetTLSData()
Definition: SDL_thread.c:123
SDL_MemoryBarrierRelease
#define SDL_MemoryBarrierRelease()
Definition: SDL_atomic.h:180
SDL_atomic_t
A type representing an atomic integer value. It is a struct so people don&#39;t accidentally use numeric ...
Definition: SDL_atomic.h:189
TLS_ALLOC_CHUNKSIZE
#define TLS_ALLOC_CHUNKSIZE
Definition: SDL_thread_c.h:78
SDL_TLSData::data
void * data
Definition: SDL_thread_c.h:72
SDL_THREAD_STATE_DETACHED
Definition: SDL_thread_c.h:48
SDL_SetThreadPriority
int SDL_SetThreadPriority(SDL_ThreadPriority priority)
Definition: SDL_thread.c:413
SDL_AtomicCAS
#define SDL_AtomicCAS
Definition: SDL_dynapi_overrides.h:72
SDL_Generic_SetTLSData
int SDL_Generic_SetTLSData(SDL_TLSData *storage)
Definition: SDL_thread.c:162
SDL_CreateSemaphore
#define SDL_CreateSemaphore
Definition: SDL_dynapi_overrides.h:270
SDL_CreateMutex
#define SDL_CreateMutex
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:40
data
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: SDL_opengl.h:1967
SDL_TLSData
Definition: SDL_thread_c.h:69
SDL_TLSData::limit
unsigned int limit
Definition: SDL_thread_c.h:70
SDL_TLSEntry::thread
SDL_threadID thread
Definition: SDL_thread.c:113
SDL_error
Definition: SDL_error_c.h:33
thread_args
Definition: SDL_thread.c:255
SDL_CreateThread
#define SDL_CreateThread
Definition: SDL_thread.c:303
SDL_SYS_SetTLSData
int SDL_SYS_SetTLSData(SDL_TLSData *data)
Definition: SDL_systls.c:33
name
GLuint const GLchar * name
Definition: SDL_opengl_glext.h:660
SDL_Thread::threadid
SDL_threadID threadid
Definition: SDL_thread_c.h:56
SDL_DetachThread
void SDL_DetachThread(SDL_Thread *thread)
Definition: SDL_thread.c:434
mutex
static SDL_mutex * mutex
Definition: testlock.c:23
SDL_InvalidParamError
#define SDL_InvalidParamError(param)
Definition: SDL_error.h:54
SDL_systhread.h
SDL_realloc
#define SDL_realloc
Definition: SDL_dynapi_overrides.h:382
SDL_SYS_GetTLSData
SDL_TLSData * SDL_SYS_GetTLSData()
Definition: SDL_systls.c:27
SDL_Thread::status
int status
Definition: SDL_thread_c.h:58
SDL_zerop
#define SDL_zerop(x)
Definition: SDL_stdinc.h:358
SDL_MemoryBarrierAcquire
#define SDL_MemoryBarrierAcquire()
Definition: SDL_atomic.h:181
SDL_thread.h
SDL_TLSEntry::next
struct SDL_TLSEntry * next
Definition: SDL_thread.c:115
SDL_THREAD_STATE_CLEANED
Definition: SDL_thread_c.h:50
SDL_TLSID
unsigned int SDL_TLSID
Definition: SDL_thread.h:52
SDL_SemPost
#define SDL_SemPost
Definition: SDL_dynapi_overrides.h:275
SDL_mutex
Definition: SDL_sysmutex.c:29
SDL_AtomicUnlock
#define SDL_AtomicUnlock
Definition: SDL_dynapi_overrides.h:71
SDL_bool
SDL_bool
Definition: SDL_stdinc.h:126
SDL_SYS_WaitThread
void SDL_SYS_WaitThread(SDL_Thread *thread)
Definition: SDL_systhread.c:60
DECLSPEC
#define DECLSPEC
Definition: SDL_internal.h:30
SDL_THREAD_STATE_ALIVE
Definition: SDL_thread_c.h:47
value
GLsizei const GLfloat * value
Definition: SDL_opengl_glext.h:698
SDL_free
void SDL_free(void *mem)
SDL_SYS_SetThreadPriority
int SDL_SYS_SetThreadPriority(SDL_ThreadPriority priority)
Definition: SDL_systhread.c:54
SDL_thread_c.h
SDL_SYS_DetachThread
void SDL_SYS_DetachThread(SDL_Thread *thread)
Definition: SDL_systhread.c:66
SDL_TRUE
Definition: SDL_stdinc.h:129
SDL_TLSCleanup
static void SDL_TLSCleanup()
Definition: SDL_thread.c:86
SDL_GetErrBuf
SDL_error * SDL_GetErrBuf(void)
Definition: SDL_thread.c:205
SDL_AtomicIncRef
#define SDL_AtomicIncRef(a)
Increment an atomic variable used as a reference count.
Definition: SDL_atomic.h:225
thread_args::func
int(* func)(void *)
Definition: SDL_thread.c:257
SDL_GetThreadID
SDL_threadID SDL_GetThreadID(SDL_Thread *thread)
Definition: SDL_thread.c:390
SDL_TLSEntry
Definition: SDL_thread.c:112
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:42
SDL_assert
#define SDL_assert(condition)
Definition: SDL_assert.h:167
NULL
#define NULL
Definition: begin_code.h:143
SDL_OutOfMemory
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
SDL_WaitThread
void SDL_WaitThread(SDL_Thread *thread, int *status)
Definition: SDL_thread.c:419
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:33
func
GLenum func
Definition: SDL_opengl_glext.h:657
thread_args::info
SDL_Thread * info
Definition: SDL_thread.c:259
SDL_generic_TLS
static SDL_TLSEntry * SDL_generic_TLS
Definition: SDL_thread.c:119
SDL_TLSData::array
struct SDL_TLSData::@27 array[1]
SDL_ThreadPriority
SDL_ThreadPriority
Definition: SDL_thread.h:59
thread_args::wait
SDL_sem * wait
Definition: SDL_thread.c:260
SDL_strdup
#define SDL_strdup
Definition: SDL_dynapi_overrides.h:403
SDL_SemWait
#define SDL_SemWait
Definition: SDL_dynapi_overrides.h:272
SDL_DestroySemaphore
#define SDL_DestroySemaphore
Definition: SDL_dynapi_overrides.h:271
SDL_RunThread
void SDL_RunThread(void *data)
Definition: SDL_thread.c:264
SDL_FALSE
Definition: SDL_stdinc.h:128
SDL_SYS_SetupThread
void SDL_SYS_SetupThread(const char *name)
Definition: SDL_systhread.c:42
SDL_AtomicSet
#define SDL_AtomicSet
Definition: SDL_dynapi_overrides.h:73
SDL_AtomicGet
#define SDL_AtomicGet
Definition: SDL_dynapi_overrides.h:74
SDL_GetThreadName
const char * SDL_GetThreadName(SDL_Thread *thread)
Definition: SDL_thread.c:403
SDL_Thread
Definition: SDL_thread_c.h:54
SDL_UnlockMutex
#define SDL_UnlockMutex
Definition: SDL_dynapi_overrides.h:268
SDL_malloc
#define SDL_malloc
Definition: SDL_dynapi_overrides.h:380
SDL_TLSData::destructor
void(* destructor)(void *)
Definition: SDL_thread_c.h:73
SDL_SpinLock
int SDL_SpinLock
Definition: SDL_atomic.h:89
thread_args::data
void * data
Definition: SDL_thread.c:258
SDL_SYS_CreateThread
int SDL_SYS_CreateThread(SDL_Thread *thread, void *args)
Definition: SDL_systhread.c:35
SDLCALL
#define SDLCALL
Definition: SDL_internal.h:31
SDL_threadID
unsigned long SDL_threadID
Definition: SDL_thread.h:49
SDL_assert.h