#include "SDL_internal.h"
#include "SDL_thread_c.h"
#include "SDL_systhread.h"
#include "../SDL_error_c.h"
static SDL_AtomicInt SDL_tls_allocated;
static SDL_AtomicInt SDL_tls_id;
void SDL_InitTLSData(void)
{
SDL_SYS_InitTLSData();
}
void *SDL_GetTLS(SDL_TLSID *id)
{
SDL_TLSData *storage;
int storage_index;
if (id == NULL) {
SDL_InvalidParamError("id");
return NULL;
}
storage_index = SDL_GetAtomicInt(id) - 1;
storage = SDL_SYS_GetTLSData();
if (!storage || storage_index < 0 || storage_index >= storage->limit) {
return NULL;
}
return storage->array[storage_index].data;
}
bool SDL_SetTLS(SDL_TLSID *id, const void *value, SDL_TLSDestructorCallback destructor)
{
SDL_TLSData *storage;
int storage_index;
if (id == NULL) {
return SDL_InvalidParamError("id");
}
SDL_InitTLSData();
storage_index = SDL_GetAtomicInt(id) - 1;
if (storage_index < 0) {
int new_id = (SDL_AtomicIncRef(&SDL_tls_id) + 1);
SDL_CompareAndSwapAtomicInt(id, 0, new_id);
storage_index = SDL_GetAtomicInt(id) - 1;
}
storage = SDL_SYS_GetTLSData();
if (!storage || storage_index >= storage->limit) {
unsigned int i, oldlimit, newlimit;
SDL_TLSData *new_storage;
oldlimit = storage ? storage->limit : 0;
newlimit = (storage_index + TLS_ALLOC_CHUNKSIZE);
new_storage = (SDL_TLSData *)SDL_realloc(storage, sizeof(*storage) + (newlimit - 1) * sizeof(storage->array[0]));
if (!new_storage) {
return false;
}
storage = new_storage;
storage->limit = newlimit;
for (i = oldlimit; i < newlimit; ++i) {
storage->array[i].data = NULL;
storage->array[i].destructor = NULL;
}
if (!SDL_SYS_SetTLSData(storage)) {
SDL_free(storage);
return false;
}
SDL_AtomicIncRef(&SDL_tls_allocated);
}
storage->array[storage_index].data = SDL_const_cast(void *, value);
storage->array[storage_index].destructor = destructor;
return true;
}
void SDL_CleanupTLS(void)
{
SDL_TLSData *storage;
storage = SDL_SYS_GetTLSData();
if (storage) {
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);
(void)SDL_AtomicDecRef(&SDL_tls_allocated);
}
}
void SDL_QuitTLSData(void)
{
SDL_CleanupTLS();
if (SDL_GetAtomicInt(&SDL_tls_allocated) == 0) {
SDL_SYS_QuitTLSData();
} else {
}
}
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;
void SDL_Generic_InitTLSData(void)
{
if (!SDL_generic_TLS_mutex) {
SDL_generic_TLS_mutex = SDL_CreateMutex();
}
}
SDL_TLSData *SDL_Generic_GetTLSData(void)
{
SDL_ThreadID thread = SDL_GetCurrentThreadID();
SDL_TLSEntry *entry;
SDL_TLSData *storage = NULL;
SDL_LockMutex(SDL_generic_TLS_mutex);
for (entry = SDL_generic_TLS; entry; entry = entry->next) {
if (entry->thread == thread) {
storage = entry->storage;
break;
}
}
SDL_UnlockMutex(SDL_generic_TLS_mutex);
return storage;
}
bool SDL_Generic_SetTLSData(SDL_TLSData *data)
{
SDL_ThreadID thread = SDL_GetCurrentThreadID();
SDL_TLSEntry *prev, *entry;
bool result = true;
SDL_LockMutex(SDL_generic_TLS_mutex);
prev = NULL;
for (entry = SDL_generic_TLS; entry; entry = entry->next) {
if (entry->thread == thread) {
if (data) {
entry->storage = data;
} else {
if (prev) {
prev->next = entry->next;
} else {
SDL_generic_TLS = entry->next;
}
SDL_free(entry);
}
break;
}
prev = entry;
}
if (!entry && data) {
entry = (SDL_TLSEntry *)SDL_malloc(sizeof(*entry));
if (entry) {
entry->thread = thread;
entry->storage = data;
entry->next = SDL_generic_TLS;
SDL_generic_TLS = entry;
} else {
result = false;
}
}
SDL_UnlockMutex(SDL_generic_TLS_mutex);
return result;
}
void SDL_Generic_QuitTLSData(void)
{
SDL_TLSEntry *entry;
SDL_assert(!SDL_generic_TLS);
if (SDL_generic_TLS) {
SDL_LockMutex(SDL_generic_TLS_mutex);
for (entry = SDL_generic_TLS; entry; ) {
SDL_TLSEntry *next = entry->next;
SDL_free(entry->storage);
SDL_free(entry);
entry = next;
}
SDL_generic_TLS = NULL;
SDL_UnlockMutex(SDL_generic_TLS_mutex);
}
if (SDL_generic_TLS_mutex) {
SDL_DestroyMutex(SDL_generic_TLS_mutex);
SDL_generic_TLS_mutex = NULL;
}
}
static SDL_error *SDL_GetStaticErrBuf(void)
{
static SDL_error SDL_global_error;
static char SDL_global_error_str[128];
SDL_global_error.str = SDL_global_error_str;
SDL_global_error.len = sizeof(SDL_global_error_str);
return &SDL_global_error;
}
#ifndef SDL_THREADS_DISABLED
static void SDLCALL SDL_FreeErrBuf(void *data)
{
SDL_error *errbuf = (SDL_error *)data;
if (errbuf->str) {
errbuf->free_func(errbuf->str);
}
errbuf->free_func(errbuf);
}
#endif
SDL_error *SDL_GetErrBuf(bool create)
{
#ifdef SDL_THREADS_DISABLED
return SDL_GetStaticErrBuf();
#else
static SDL_TLSID tls_errbuf;
SDL_error *errbuf;
errbuf = (SDL_error *)SDL_GetTLS(&tls_errbuf);
if (!errbuf) {
if (!create) {
return NULL;
}
SDL_realloc_func realloc_func;
SDL_free_func free_func;
SDL_GetOriginalMemoryFunctions(NULL, NULL, &realloc_func, &free_func);
errbuf = (SDL_error *)realloc_func(NULL, sizeof(*errbuf));
if (!errbuf) {
return SDL_GetStaticErrBuf();
}
SDL_zerop(errbuf);
errbuf->realloc_func = realloc_func;
errbuf->free_func = free_func;
SDL_SetTLS(&tls_errbuf, errbuf, SDL_FreeErrBuf);
}
return errbuf;
#endif
}
static bool ThreadValid(SDL_Thread *thread)
{
return SDL_ObjectValid(thread, SDL_OBJECT_TYPE_THREAD);
}
void SDL_RunThread(SDL_Thread *thread)
{
void *userdata = thread->userdata;
int(SDLCALL *userfunc)(void *) = thread->userfunc;
int *statusloc = &thread->status;
SDL_SYS_SetupThread(thread->name);
thread->threadid = SDL_GetCurrentThreadID();
*statusloc = userfunc(userdata);
SDL_CleanupTLS();
if (!SDL_CompareAndSwapAtomicInt(&thread->state, SDL_THREAD_ALIVE, SDL_THREAD_COMPLETE)) {
if (SDL_GetThreadState(thread) == SDL_THREAD_DETACHED) {
SDL_free(thread->name);
SDL_free(thread);
}
}
}
SDL_Thread *SDL_CreateThreadWithPropertiesRuntime(SDL_PropertiesID props,
SDL_FunctionPointer pfnBeginThread,
SDL_FunctionPointer pfnEndThread)
{
#if !defined(SDL_PLATFORM_WINDOWS)
if (pfnBeginThread || pfnEndThread) {
SDL_SetError("_beginthreadex/_endthreadex not supported on this platform");
return NULL;
}
#endif
SDL_ThreadFunction fn = (SDL_ThreadFunction) SDL_GetPointerProperty(props, SDL_PROP_THREAD_CREATE_ENTRY_FUNCTION_POINTER, NULL);
const char *name = SDL_GetStringProperty(props, SDL_PROP_THREAD_CREATE_NAME_STRING, NULL);
const size_t stacksize = (size_t) SDL_GetNumberProperty(props, SDL_PROP_THREAD_CREATE_STACKSIZE_NUMBER, 0);
void *userdata = SDL_GetPointerProperty(props, SDL_PROP_THREAD_CREATE_USERDATA_POINTER, NULL);
if (!fn) {
SDL_SetError("Thread entry function is NULL");
return NULL;
}
SDL_InitMainThread();
SDL_Thread *thread = (SDL_Thread *)SDL_calloc(1, sizeof(*thread));
if (!thread) {
return NULL;
}
thread->status = -1;
SDL_SetAtomicInt(&thread->state, SDL_THREAD_ALIVE);
if (name) {
thread->name = SDL_strdup(name);
if (!thread->name) {
SDL_free(thread);
return NULL;
}
}
thread->userfunc = fn;
thread->userdata = userdata;
thread->stacksize = stacksize;
SDL_SetObjectValid(thread, SDL_OBJECT_TYPE_THREAD, true);
if (!SDL_SYS_CreateThread(thread, pfnBeginThread, pfnEndThread)) {
SDL_SetObjectValid(thread, SDL_OBJECT_TYPE_THREAD, false);
SDL_free(thread->name);
SDL_free(thread);
thread = NULL;
}
return thread;
}
SDL_Thread *SDL_CreateThreadRuntime(SDL_ThreadFunction fn,
const char *name, void *userdata,
SDL_FunctionPointer pfnBeginThread,
SDL_FunctionPointer pfnEndThread)
{
const SDL_PropertiesID props = SDL_CreateProperties();
SDL_SetPointerProperty(props, SDL_PROP_THREAD_CREATE_ENTRY_FUNCTION_POINTER, (void *) fn);
SDL_SetStringProperty(props, SDL_PROP_THREAD_CREATE_NAME_STRING, name);
SDL_SetPointerProperty(props, SDL_PROP_THREAD_CREATE_USERDATA_POINTER, userdata);
SDL_Thread *thread = SDL_CreateThreadWithPropertiesRuntime(props, pfnBeginThread, pfnEndThread);
SDL_DestroyProperties(props);
return thread;
}
SDL_Thread *SDL_CreateThreadWithStackSize(SDL_ThreadFunction fn, const char *name, size_t stacksize, void *userdata)
{
const SDL_PropertiesID props = SDL_CreateProperties();
SDL_SetPointerProperty(props, SDL_PROP_THREAD_CREATE_ENTRY_FUNCTION_POINTER, (void *) fn);
SDL_SetStringProperty(props, SDL_PROP_THREAD_CREATE_NAME_STRING, name);
SDL_SetPointerProperty(props, SDL_PROP_THREAD_CREATE_USERDATA_POINTER, userdata);
SDL_SetNumberProperty(props, SDL_PROP_THREAD_CREATE_STACKSIZE_NUMBER, (Sint64) stacksize);
SDL_Thread *thread = SDL_CreateThreadWithProperties(props);
SDL_DestroyProperties(props);
return thread;
}
SDL_ThreadID SDL_GetThreadID(SDL_Thread *thread)
{
SDL_ThreadID id = 0;
if (thread) {
if (ThreadValid(thread)) {
id = thread->threadid;
}
} else {
id = SDL_GetCurrentThreadID();
}
return id;
}
const char *SDL_GetThreadName(SDL_Thread *thread)
{
if (ThreadValid(thread)) {
return SDL_GetPersistentString(thread->name);
} else {
return NULL;
}
}
bool SDL_SetCurrentThreadPriority(SDL_ThreadPriority priority)
{
return SDL_SYS_SetThreadPriority(priority);
}
void SDL_WaitThread(SDL_Thread *thread, int *status)
{
if (!ThreadValid(thread)) {
if (status) {
*status = -1;
}
return;
}
SDL_SYS_WaitThread(thread);
if (status) {
*status = thread->status;
}
SDL_SetObjectValid(thread, SDL_OBJECT_TYPE_THREAD, false);
SDL_free(thread->name);
SDL_free(thread);
}
SDL_ThreadState SDL_GetThreadState(SDL_Thread *thread)
{
if (!ThreadValid(thread)) {
return SDL_THREAD_UNKNOWN;
}
return (SDL_ThreadState)SDL_GetAtomicInt(&thread->state);
}
void SDL_DetachThread(SDL_Thread *thread)
{
if (!ThreadValid(thread)) {
return;
}
SDL_SetObjectValid(thread, SDL_OBJECT_TYPE_THREAD, false);
if (SDL_CompareAndSwapAtomicInt(&thread->state, SDL_THREAD_ALIVE, SDL_THREAD_DETACHED)) {
SDL_SYS_DetachThread(thread);
} else {
SDL_ThreadState thread_state = SDL_GetThreadState(thread);
if (thread_state == SDL_THREAD_DETACHED) {
return;
} else if (thread_state == SDL_THREAD_COMPLETE) {
SDL_WaitThread(thread, NULL);
}
}
}
void SDL_WaitSemaphore(SDL_Semaphore *sem)
{
SDL_WaitSemaphoreTimeoutNS(sem, -1);
}
bool SDL_TryWaitSemaphore(SDL_Semaphore *sem)
{
return SDL_WaitSemaphoreTimeoutNS(sem, 0);
}
bool SDL_WaitSemaphoreTimeout(SDL_Semaphore *sem, Sint32 timeoutMS)
{
Sint64 timeoutNS;
if (timeoutMS >= 0) {
timeoutNS = SDL_MS_TO_NS(timeoutMS);
} else {
timeoutNS = -1;
}
return SDL_WaitSemaphoreTimeoutNS(sem, timeoutNS);
}
void SDL_WaitCondition(SDL_Condition *cond, SDL_Mutex *mutex)
{
SDL_WaitConditionTimeoutNS(cond, mutex, -1);
}
bool SDL_WaitConditionTimeout(SDL_Condition *cond, SDL_Mutex *mutex, Sint32 timeoutMS)
{
Sint64 timeoutNS;
if (timeoutMS >= 0) {
timeoutNS = SDL_MS_TO_NS(timeoutMS);
} else {
timeoutNS = -1;
}
return SDL_WaitConditionTimeoutNS(cond, mutex, timeoutNS);
}
bool SDL_ShouldInit(SDL_InitState *state)
{
while (SDL_GetAtomicInt(&state->status) != SDL_INIT_STATUS_INITIALIZED) {
if (SDL_CompareAndSwapAtomicInt(&state->status, SDL_INIT_STATUS_UNINITIALIZED, SDL_INIT_STATUS_INITIALIZING)) {
state->thread = SDL_GetCurrentThreadID();
return true;
}
SDL_Delay(1);
}
return false;
}
bool SDL_ShouldQuit(SDL_InitState *state)
{
while (SDL_GetAtomicInt(&state->status) != SDL_INIT_STATUS_UNINITIALIZED) {
if (SDL_CompareAndSwapAtomicInt(&state->status, SDL_INIT_STATUS_INITIALIZED, SDL_INIT_STATUS_UNINITIALIZING)) {
state->thread = SDL_GetCurrentThreadID();
return true;
}
SDL_Delay(1);
}
return false;
}
void SDL_SetInitialized(SDL_InitState *state, bool initialized)
{
SDL_assert(state->thread == SDL_GetCurrentThreadID());
if (initialized) {
SDL_SetAtomicInt(&state->status, SDL_INIT_STATUS_INITIALIZED);
} else {
SDL_SetAtomicInt(&state->status, SDL_INIT_STATUS_UNINITIALIZED);
}
}