summaryrefslogtreecommitdiffstatshomepage
path: root/3rdparty/SDL2/src/thread/SDL_thread.c
diff options
context:
space:
mode:
author Miodrag Milanovic <mmicko@gmail.com>2016-02-27 16:28:05 +0100
committer Miodrag Milanovic <mmicko@gmail.com>2016-02-27 16:53:49 +0100
commitcf6dc7d370407914020f3e623719f09c8668c692 (patch)
tree46b3416d091b4c09b50fba7ffcbd310aed032cc8 /3rdparty/SDL2/src/thread/SDL_thread.c
parent64135e73f973d9b95beb0c60b65feb34fbfec64a (diff)
Placed SDL2 source since we need it on some platforms (nw)
Diffstat (limited to '3rdparty/SDL2/src/thread/SDL_thread.c')
-rw-r--r--3rdparty/SDL2/src/thread/SDL_thread.c456
1 files changed, 456 insertions, 0 deletions
diff --git a/3rdparty/SDL2/src/thread/SDL_thread.c b/3rdparty/SDL2/src/thread/SDL_thread.c
new file mode 100644
index 00000000000..e66c5819cde
--- /dev/null
+++ b/3rdparty/SDL2/src/thread/SDL_thread.c
@@ -0,0 +1,456 @@
+/*
+ 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: */