From 9b3754671d0c1d66d7699a71ce0398f1c6af9756 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Wed, 10 Jul 2013 02:32:04 -0700 Subject: [PATCH] Implemented an API for thread-local storage: SDL_TLSCreate(), SDL_TLSSet(), SDL_TLSGet() --- VisualC/SDL/SDL_VS2008.vcproj | 4 + VisualC/SDL/SDL_VS2010.vcxproj | 3 +- VisualC/SDL/SDL_VS2012.vcxproj | 3 +- Xcode-iOS/SDL/SDL.xcodeproj/project.pbxproj | 4 + Xcode/SDL/SDL.xcodeproj/project.pbxproj | 8 + configure | 25 ++- configure.in | 8 +- include/SDL_thread.h | 61 +++++++ src/thread/SDL_thread.c | 167 +++----------------- src/thread/beos/SDL_systls.c | 106 +++++++++++++ src/thread/generic/SDL_systls.c | 163 +++++++++++++++++++ src/thread/pthread/SDL_systls.c | 101 ++++++++++++ src/thread/windows/SDL_systls.c | 106 +++++++++++++ test/testthread.c | 13 +- 14 files changed, 617 insertions(+), 155 deletions(-) create mode 100644 src/thread/beos/SDL_systls.c create mode 100644 src/thread/generic/SDL_systls.c create mode 100644 src/thread/pthread/SDL_systls.c create mode 100644 src/thread/windows/SDL_systls.c diff --git a/VisualC/SDL/SDL_VS2008.vcproj b/VisualC/SDL/SDL_VS2008.vcproj index 968fee2cb..9481c6380 100644 --- a/VisualC/SDL/SDL_VS2008.vcproj +++ b/VisualC/SDL/SDL_VS2008.vcproj @@ -1132,6 +1132,10 @@ RelativePath="..\..\src\thread\windows\SDL_systhread.c" > + + diff --git a/VisualC/SDL/SDL_VS2010.vcxproj b/VisualC/SDL/SDL_VS2010.vcxproj index b6e995c54..72b111c71 100644 --- a/VisualC/SDL/SDL_VS2010.vcxproj +++ b/VisualC/SDL/SDL_VS2010.vcxproj @@ -438,6 +438,7 @@ + @@ -462,4 +463,4 @@ - \ No newline at end of file + diff --git a/VisualC/SDL/SDL_VS2012.vcxproj b/VisualC/SDL/SDL_VS2012.vcxproj index efe911951..74e470051 100644 --- a/VisualC/SDL/SDL_VS2012.vcxproj +++ b/VisualC/SDL/SDL_VS2012.vcxproj @@ -441,6 +441,7 @@ + @@ -466,4 +467,4 @@ - \ No newline at end of file + diff --git a/Xcode-iOS/SDL/SDL.xcodeproj/project.pbxproj b/Xcode-iOS/SDL/SDL.xcodeproj/project.pbxproj index 64f7d6d3b..bdad57c6a 100755 --- a/Xcode-iOS/SDL/SDL.xcodeproj/project.pbxproj +++ b/Xcode-iOS/SDL/SDL.xcodeproj/project.pbxproj @@ -76,6 +76,7 @@ 93CB792613FC5F5300BD3E05 /* SDL_uikitviewcontroller.m in Sources */ = {isa = PBXBuildFile; fileRef = 93CB792513FC5F5300BD3E05 /* SDL_uikitviewcontroller.m */; }; AA0AD06216647BBB00CE5896 /* SDL_gamecontroller.c in Sources */ = {isa = PBXBuildFile; fileRef = AA0AD06116647BBB00CE5896 /* SDL_gamecontroller.c */; }; AA0AD06516647BD400CE5896 /* SDL_gamecontroller.h in Headers */ = {isa = PBXBuildFile; fileRef = AA0AD06416647BD400CE5896 /* SDL_gamecontroller.h */; }; + AA0F8495178D5F1A00823F9D /* SDL_systls.c in Sources */ = {isa = PBXBuildFile; fileRef = AA0F8494178D5F1A00823F9D /* SDL_systls.c */; }; AA126AD41617C5E7005ABC8F /* SDL_uikitmodes.h in Headers */ = {isa = PBXBuildFile; fileRef = AA126AD21617C5E6005ABC8F /* SDL_uikitmodes.h */; }; AA126AD51617C5E7005ABC8F /* SDL_uikitmodes.m in Sources */ = {isa = PBXBuildFile; fileRef = AA126AD31617C5E6005ABC8F /* SDL_uikitmodes.m */; }; AA628ADB159369E3005138DD /* SDL_rotate.c in Sources */ = {isa = PBXBuildFile; fileRef = AA628AD9159369E3005138DD /* SDL_rotate.c */; }; @@ -268,6 +269,7 @@ 93CB792513FC5F5300BD3E05 /* SDL_uikitviewcontroller.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDL_uikitviewcontroller.m; sourceTree = ""; }; AA0AD06116647BBB00CE5896 /* SDL_gamecontroller.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_gamecontroller.c; sourceTree = ""; }; AA0AD06416647BD400CE5896 /* SDL_gamecontroller.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_gamecontroller.h; sourceTree = ""; }; + AA0F8494178D5F1A00823F9D /* SDL_systls.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_systls.c; sourceTree = ""; }; AA126AD21617C5E6005ABC8F /* SDL_uikitmodes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_uikitmodes.h; sourceTree = ""; }; AA126AD31617C5E6005ABC8F /* SDL_uikitmodes.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDL_uikitmodes.m; sourceTree = ""; }; AA628AD9159369E3005138DD /* SDL_rotate.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_rotate.c; sourceTree = ""; }; @@ -851,6 +853,7 @@ FD99BA0A0DD52EDC00FB1D6B /* SDL_syssem.c */, FD99BA0B0DD52EDC00FB1D6B /* SDL_systhread.c */, FD99BA0C0DD52EDC00FB1D6B /* SDL_systhread_c.h */, + AA0F8494178D5F1A00823F9D /* SDL_systls.c */, ); path = pthread; sourceTree = ""; @@ -1189,6 +1192,7 @@ AA704DD7162AA90A0076D1C1 /* SDL_dropevents.c in Sources */, AABCC3951640643D00AB8930 /* SDL_uikitmessagebox.m in Sources */, AA0AD06216647BBB00CE5896 /* SDL_gamecontroller.c in Sources */, + AA0F8495178D5F1A00823F9D /* SDL_systls.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Xcode/SDL/SDL.xcodeproj/project.pbxproj b/Xcode/SDL/SDL.xcodeproj/project.pbxproj index 48c4c0edf..c2552eff4 100755 --- a/Xcode/SDL/SDL.xcodeproj/project.pbxproj +++ b/Xcode/SDL/SDL.xcodeproj/project.pbxproj @@ -398,6 +398,9 @@ A77E6EB4167AB0A90010E40B /* SDL_gamecontroller.h in Headers */ = {isa = PBXBuildFile; fileRef = A77E6EB3167AB0A90010E40B /* SDL_gamecontroller.h */; settings = {ATTRIBUTES = (Public, ); }; }; A77E6EB5167AB0A90010E40B /* SDL_gamecontroller.h in Headers */ = {isa = PBXBuildFile; fileRef = A77E6EB3167AB0A90010E40B /* SDL_gamecontroller.h */; }; AA0AD09D16648D1700CE5896 /* SDL_gamecontroller.c in Sources */ = {isa = PBXBuildFile; fileRef = BBFC088A164C6514003E6A99 /* SDL_gamecontroller.c */; }; + AA0F8491178D5ECC00823F9D /* SDL_systls.c in Sources */ = {isa = PBXBuildFile; fileRef = AA0F8490178D5ECC00823F9D /* SDL_systls.c */; }; + AA0F8492178D5ECC00823F9D /* SDL_systls.c in Sources */ = {isa = PBXBuildFile; fileRef = AA0F8490178D5ECC00823F9D /* SDL_systls.c */; }; + AA0F8493178D5ECC00823F9D /* SDL_systls.c in Sources */ = {isa = PBXBuildFile; fileRef = AA0F8490178D5ECC00823F9D /* SDL_systls.c */; }; AA41F88014B8F1F500993C4F /* SDL_dropevents.c in Sources */ = {isa = PBXBuildFile; fileRef = 566CDE8E148F0AC200C5A9BB /* SDL_dropevents.c */; }; AA628ACA159367B7005138DD /* SDL_rotate.c in Sources */ = {isa = PBXBuildFile; fileRef = AA628AC8159367B7005138DD /* SDL_rotate.c */; }; AA628ACB159367B7005138DD /* SDL_rotate.c in Sources */ = {isa = PBXBuildFile; fileRef = AA628AC8159367B7005138DD /* SDL_rotate.c */; }; @@ -984,6 +987,7 @@ 566CDE8D148F0AC200C5A9BB /* SDL_dropevents_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_dropevents_c.h; sourceTree = ""; }; 566CDE8E148F0AC200C5A9BB /* SDL_dropevents.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_dropevents.c; sourceTree = ""; }; A77E6EB3167AB0A90010E40B /* SDL_gamecontroller.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_gamecontroller.h; sourceTree = ""; }; + AA0F8490178D5ECC00823F9D /* SDL_systls.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_systls.c; sourceTree = ""; }; AA628AC8159367B7005138DD /* SDL_rotate.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_rotate.c; sourceTree = ""; }; AA628AC9159367B7005138DD /* SDL_rotate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_rotate.h; sourceTree = ""; }; AA628ACF159367F2005138DD /* SDL_x11xinput2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_x11xinput2.c; sourceTree = ""; }; @@ -1462,6 +1466,7 @@ 04BDFE8112E6671800899322 /* SDL_syssem.c */, 04BDFE8212E6671800899322 /* SDL_systhread.c */, 04BDFE8312E6671800899322 /* SDL_systhread_c.h */, + AA0F8490178D5ECC00823F9D /* SDL_systls.c */, ); path = pthread; sourceTree = ""; @@ -2403,6 +2408,7 @@ AA9E4093163BE51E007A2AD0 /* SDL_x11messagebox.c in Sources */, AABCC38F164063D200AB8930 /* SDL_cocoamessagebox.m in Sources */, AA0AD09D16648D1700CE5896 /* SDL_gamecontroller.c in Sources */, + AA0F8491178D5ECC00823F9D /* SDL_systls.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2519,6 +2525,7 @@ AA628AD2159367F2005138DD /* SDL_x11xinput2.c in Sources */, AA9E4094163BE51E007A2AD0 /* SDL_x11messagebox.c in Sources */, AABCC390164063D200AB8930 /* SDL_cocoamessagebox.m in Sources */, + AA0F8492178D5ECC00823F9D /* SDL_systls.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2635,6 +2642,7 @@ DB31406817554B71006C0E22 /* SDL_x11xinput2.c in Sources */, DB31406917554B71006C0E22 /* SDL_x11messagebox.c in Sources */, DB31406A17554B71006C0E22 /* SDL_cocoamessagebox.m in Sources */, + AA0F8493178D5ECC00823F9D /* SDL_systls.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/configure b/configure index 97ab2a3fe..8d824ecca 100755 --- a/configure +++ b/configure @@ -1511,7 +1511,7 @@ Optional Features: --enable-sse use SSE assembly routines [[default=yes]] --enable-sse2 use SSE2 assembly routines [[default=no]] --enable-altivec use Altivec assembly routines [[default=yes]] - --enable-oss support the OSS audio API [[default=yes]] + --enable-oss support the OSS audio API [[default=maybe]] --enable-alsa support the ALSA audio API [[default=yes]] --disable-alsatest Do not try to compile and run a test Alsa program --enable-alsa-shared dynamically load ALSA audio support [[default=yes]] @@ -17535,9 +17535,21 @@ CheckOSS() if test "${enable_oss+set}" = set; then : enableval=$enable_oss; else - enable_oss=yes + enable_oss=maybe fi + + # OpenBSD "has" OSS, but it's not really for app use. They want you to + # use sndio instead. So on there, we default to disabled. You can force + # it on if you really want, though. + if test x$enable_oss = xmaybe; then + enable_oss=yes + case "$host" in + *-*-openbsd*) + enable_oss=no;; + esac + fi + if test x$enable_audio = xyes -a x$enable_oss = xyes; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for OSS audio support" >&5 $as_echo_n "checking for OSS audio support... " >&6; } @@ -21423,6 +21435,9 @@ $as_echo "$has_pthread_set_name_np" >&6; } # We can fake these with semaphores and mutexes if necessary SOURCES="$SOURCES $srcdir/src/thread/pthread/SDL_syscond.c" + # Thread local storage + SOURCES="$SOURCES $srcdir/src/thread/pthread/SDL_systls.c" + have_threads=yes fi fi @@ -22206,7 +22221,7 @@ $as_echo "#define SDL_AUDIO_DRIVER_SUNAUDIO 1" >>confdefs.h SOURCES="$SOURCES $srcdir/src/audio/sun/*.c" have_audio=yes ;; - netbsd|openbsd) + netbsd) # Don't use this on OpenBSD, it's busted. $as_echo "#define SDL_AUDIO_DRIVER_BSD 1" >>confdefs.h @@ -22367,9 +22382,7 @@ $as_echo "#define SDL_POWER_WINDOWS 1" >>confdefs.h $as_echo "#define SDL_THREAD_WINDOWS 1" >>confdefs.h - SOURCES="$SOURCES $srcdir/src/thread/windows/SDL_sysmutex.c" - SOURCES="$SOURCES $srcdir/src/thread/windows/SDL_syssem.c" - SOURCES="$SOURCES $srcdir/src/thread/windows/SDL_systhread.c" + SOURCES="$SOURCES $srcdir/src/thread/windows/*.c" SOURCES="$SOURCES $srcdir/src/thread/generic/SDL_syscond.c" have_threads=yes fi diff --git a/configure.in b/configure.in index 825f5ba16..b795bcda5 100644 --- a/configure.in +++ b/configure.in @@ -657,6 +657,7 @@ AC_HELP_STRING([--enable-oss], [support the OSS audio API [[default=maybe]]]), case "$host" in *-*-openbsd*) enable_oss=no;; + esac fi if test x$enable_audio = xyes -a x$enable_oss = xyes; then @@ -2019,6 +2020,9 @@ AC_HELP_STRING([--enable-pthread-sem], [use pthread semaphores [[default=yes]]]) # We can fake these with semaphores and mutexes if necessary SOURCES="$SOURCES $srcdir/src/thread/pthread/SDL_syscond.c" + # Thread local storage + SOURCES="$SOURCES $srcdir/src/thread/pthread/SDL_systls.c" + have_threads=yes fi fi @@ -2470,9 +2474,7 @@ AC_HELP_STRING([--enable-render-d3d], [enable the Direct3D render driver [[defau # Set up files for the thread library if test x$enable_threads = xyes; then AC_DEFINE(SDL_THREAD_WINDOWS, 1, [ ]) - SOURCES="$SOURCES $srcdir/src/thread/windows/SDL_sysmutex.c" - SOURCES="$SOURCES $srcdir/src/thread/windows/SDL_syssem.c" - SOURCES="$SOURCES $srcdir/src/thread/windows/SDL_systhread.c" + SOURCES="$SOURCES $srcdir/src/thread/windows/*.c" SOURCES="$SOURCES $srcdir/src/thread/generic/SDL_syscond.c" have_threads=yes fi diff --git a/include/SDL_thread.h b/include/SDL_thread.h index 6015a071e..b1ebe22da 100644 --- a/include/SDL_thread.h +++ b/include/SDL_thread.h @@ -32,6 +32,7 @@ #include "SDL_error.h" /* Thread synchronization primitives */ +#include "SDL_atomic.h" #include "SDL_mutex.h" #include "begin_code.h" @@ -47,6 +48,9 @@ typedef struct SDL_Thread SDL_Thread; /* The SDL thread ID */ typedef unsigned long SDL_threadID; +/* Thread local storage ID */ +typedef int SDL_TLSID; + /* The SDL thread priority * * Note: On many systems you require special privileges to set high priority. @@ -166,6 +170,63 @@ extern DECLSPEC int SDLCALL SDL_SetThreadPriority(SDL_ThreadPriority priority); */ extern DECLSPEC void SDLCALL SDL_WaitThread(SDL_Thread * thread, int *status); +/** + * \brief Create an identifier that is globally visible to all threads but refers to data that is thread-specific. + * + * \return The newly created thread local storage identifier, or 0 on error + * + * \code + * static SDL_SpinLock tls_lock; + * static SDL_TLSID thread_local_storage; + * + * 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); + * } + * + * void *GetMyThreadData(void) + * { + * return SDL_TLSGet(thread_local_storage); + * } + * \endcode + * + * \sa SDL_TLSGet() + * \sa SDL_TLSSet() + */ +extern DECLSPEC SDL_TLSID SDLCALL SDL_TLSCreate(); + +/** + * \brief Get the value associated with a thread local storage ID for the current thread. + * + * \param id The thread local storage ID + * + * \return The value associated with the ID for the current thread, or NULL if no value has been set. + * + * \sa SDL_TLSCreate() + * \sa SDL_TLSSet() + */ +extern DECLSPEC void * SDLCALL SDL_TLSGet(SDL_TLSID id); + +/** + * \brief Set the value associated with a thread local storage ID for the current thread. + * + * \param id The thread local storage ID + * \param value The value to associate with the ID for the current thread + * + * \return 0 on success, -1 on error + * + * \sa SDL_TLSCreate() + * \sa SDL_TLSGet() + */ +extern DECLSPEC int SDLCALL SDL_TLSSet(SDL_TLSID id, const void *value); + /* Ends C function definitions when using C++ */ #ifdef __cplusplus diff --git a/src/thread/SDL_thread.c b/src/thread/SDL_thread.c index 61e252c17..0cfd90f31 100644 --- a/src/thread/SDL_thread.c +++ b/src/thread/SDL_thread.c @@ -22,158 +22,46 @@ /* System independent thread management routines for SDL */ -#include "SDL_mutex.h" #include "SDL_thread.h" #include "SDL_thread_c.h" #include "SDL_systhread.h" #include "../SDL_error_c.h" -#define ARRAY_CHUNKSIZE 32 -/* The array of threads currently active in the application - (except the main thread) - The manipulation of an array here is safer than using a linked list. -*/ -static int SDL_maxthreads = 0; -static int SDL_numthreads = 0; -static SDL_Thread **SDL_Threads = NULL; -static SDL_mutex *thread_lock = NULL; - -static int -SDL_ThreadsInit(void) -{ - int retval; - - retval = 0; - thread_lock = SDL_CreateMutex(); - if (thread_lock == NULL) { - retval = -1; - } - return (retval); -} - -/* This should never be called... - If this is called by SDL_Quit(), we don't know whether or not we should - clean up threads here. If any threads are still running after this call, - they will no longer have access to any per-thread data. - */ -#if 0 -static void -SDL_ThreadsQuit(void) -{ - SDL_mutex *mutex; - - mutex = thread_lock; - thread_lock = NULL; - if (mutex != NULL) { - SDL_DestroyMutex(mutex); - } -} -#endif - -/* Routines for manipulating the thread list */ -static void -SDL_AddThread(SDL_Thread * thread) -{ - /* WARNING: - If the very first threads are created simultaneously, then - there could be a race condition causing memory corruption. - In practice, this isn't a problem because by definition there - is only one thread running the first time this is called. - */ - if (!thread_lock) { - if (SDL_ThreadsInit() < 0) { - return; - } - } - SDL_LockMutex(thread_lock); - - /* Expand the list of threads, if necessary */ -#ifdef DEBUG_THREADS - printf("Adding thread (%d already - %d max)\n", - SDL_numthreads, SDL_maxthreads); -#endif - if (SDL_numthreads == SDL_maxthreads) { - SDL_Thread **threads; - threads = (SDL_Thread **) SDL_realloc(SDL_Threads, - (SDL_maxthreads + - ARRAY_CHUNKSIZE) * - (sizeof *threads)); - if (threads == NULL) { - SDL_OutOfMemory(); - goto done; - } - SDL_maxthreads += ARRAY_CHUNKSIZE; - SDL_Threads = threads; - } - SDL_Threads[SDL_numthreads++] = thread; - done: - SDL_mutexV(thread_lock); -} - -static void -SDL_DelThread(SDL_Thread * thread) -{ - int i; - - if (!thread_lock) { - return; - } - SDL_LockMutex(thread_lock); - for (i = 0; i < SDL_numthreads; ++i) { - if (thread == SDL_Threads[i]) { - break; - } - } - if (i < SDL_numthreads) { - if (--SDL_numthreads > 0) { - while (i < SDL_numthreads) { - SDL_Threads[i] = SDL_Threads[i + 1]; - ++i; - } - } else { - SDL_maxthreads = 0; - SDL_free(SDL_Threads); - SDL_Threads = NULL; - } -#ifdef DEBUG_THREADS - printf("Deleting thread (%d left - %d max)\n", - SDL_numthreads, SDL_maxthreads); -#endif - } - SDL_mutexV(thread_lock); - -#if 0 /* There could be memory corruption if another thread is starting */ - if (SDL_Threads == NULL) { - SDL_ThreadsQuit(); - } -#endif -} - -/* The default (non-thread-safe) global error variable */ -static SDL_error SDL_global_error; /* Routine to get the thread-specific error variable */ SDL_error * SDL_GetErrBuf(void) { + static SDL_SpinLock spinlock; + static SDL_bool tls_being_created; + static SDL_TLSID tls_errbuf; + static SDL_error SDL_global_errbuf; SDL_error *errbuf; - errbuf = &SDL_global_error; - if (SDL_Threads) { - int i; - SDL_threadID this_thread; + if (!tls_errbuf && !tls_being_created) { + SDL_AtomicLock(&spinlock); + if (!tls_errbuf) { + /* SDL_TLSCreate() could fail and call SDL_SetError() */ + tls_being_created = SDL_TRUE; + tls_errbuf = SDL_TLSCreate(); + tls_being_created = SDL_FALSE; + } + SDL_AtomicUnlock(&spinlock); + } + if (!tls_errbuf) { + return &SDL_global_errbuf; + } - this_thread = SDL_ThreadID(); - SDL_LockMutex(thread_lock); - for (i = 0; i < SDL_numthreads; ++i) { - if (this_thread == SDL_Threads[i]->threadid) { - errbuf = &SDL_Threads[i]->errbuf; - break; - } + errbuf = SDL_TLSGet(tls_errbuf); + if (!errbuf) { + errbuf = (SDL_error *)SDL_malloc(sizeof(*errbuf)); + if (!errbuf) { + return &SDL_global_errbuf; } - SDL_mutexV(thread_lock); + SDL_zerop(errbuf); + SDL_TLSSet(tls_errbuf, errbuf); } - return (errbuf); + return errbuf; } @@ -264,9 +152,6 @@ SDL_CreateThread(int (SDLCALL * fn) (void *), return (NULL); } - /* Add the thread to the list of available threads */ - SDL_AddThread(thread); - /* Create the thread and go! */ #ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD ret = SDL_SYS_CreateThread(thread, args, pfnBeginThread, pfnEndThread); @@ -278,7 +163,6 @@ SDL_CreateThread(int (SDLCALL * fn) (void *), SDL_SemWait(args->wait); } else { /* Oops, failed. Gotta free everything */ - SDL_DelThread(thread); SDL_free(thread->name); SDL_free(thread); thread = NULL; @@ -323,7 +207,6 @@ SDL_WaitThread(SDL_Thread * thread, int *status) if (status) { *status = thread->status; } - SDL_DelThread(thread); SDL_free(thread->name); SDL_free(thread); } diff --git a/src/thread/beos/SDL_systls.c b/src/thread/beos/SDL_systls.c new file mode 100644 index 000000000..fb7130f40 --- /dev/null +++ b/src/thread/beos/SDL_systls.c @@ -0,0 +1,106 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2013 Sam Lantinga + + 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_config.h" +#include "SDL_thread.h" + +#if SDL_THREAD_BEOS + +#include + + +#define TLS_ALLOC_CHUNKSIZE 8 + +typedef struct { + int limit; + void *data[1]; +} SDL_TLSData; + +static SDL_SpinLock tls_lock; +static int32 thread_local_storage = B_NO_MEMORY; +static SDL_atomic_t tls_id; + + +SDL_TLSID +SDL_TLSCreate() +{ + if (thread_local_storage == B_NO_MEMORY) { + SDL_AtomicLock(&tls_lock); + if (thread_local_storage == B_NO_MEMORY) { + thread_local_storage = tls_allocate(); + if (thread_local_storage == B_NO_MEMORY) { + SDL_SetError("tls_allocate() failed"); + SDL_AtomicUnlock(&tls_lock); + return 0; + } + } + SDL_AtomicUnlock(&tls_lock); + } + return SDL_AtomicIncRef(&tls_id)+1; +} + +void * +SDL_TLSGet(SDL_TLSID id) +{ + SDL_TLSData *data; + + data = (SDL_TLSData *)tls_get(thread_local_storage); + if (!data || id <= 0 || id > data->limit) { + return NULL; + } + return data->data[id-1]; +} + +int +SDL_TLSSet(SDL_TLSID id, const void *value) +{ + SDL_TLSData *data; + + if (thread_local_storage == B_NO_MEMORY || id <= 0) { + return SDL_InvalidParamError(id); + } + + data = (SDL_TLSData *)tls_get(thread_local_storage); + if (!data || id > data->limit) { + int i, oldlimit, newlimit; + + oldlimit = data ? data->limit : 0; + newlimit = (id + TLS_ALLOC_CHUNKSIZE); + data = (SDL_TLSData *)SDL_realloc(data, sizeof(*data)+(newlimit-1)*sizeof(void*)); + if (!data) { + return SDL_OutOfMemory(); + } + data->limit = newlimit; + for (i = oldlimit; i < newlimit; ++i) { + data->data[i] = NULL; + } + if (!tls_set(thread_local_storage, data)) { + return SDL_SetError("TlsSetValue() failed"); + } + } + + data->data[id-1] = SDL_const_cast(void*, value); + return 0; +} + +#endif /* SDL_THREAD_BEOS */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/thread/generic/SDL_systls.c b/src/thread/generic/SDL_systls.c new file mode 100644 index 000000000..febc63e45 --- /dev/null +++ b/src/thread/generic/SDL_systls.c @@ -0,0 +1,163 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2013 Sam Lantinga + + 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_config.h" +#include "SDL_thread.h" + +/* 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. +*/ + +#define TLS_ALLOC_CHUNKSIZE 8 + +typedef struct { + int limit; + void *data[1]; +} SDL_TLSData; + +typedef struct SDL_TLSEntry { + SDL_threadID thread; + SDL_TLSData *data; + struct SDL_TLSEntry *next; +} SDL_TLSEntry; + +static SDL_SpinLock tls_lock; +static SDL_mutex *tls_mutex; +static SDL_TLSEntry *thread_local_storage; +static SDL_atomic_t tls_id; + + +static SDL_TLSData *GetTLSData() +{ + SDL_threadID thread = SDL_ThreadID(); + SDL_TLSEntry *entry; + SDL_TLSData *data = NULL; + + if (!tls_mutex) { + SDL_AtomicLock(&tls_lock); + if (!tls_mutex) { + tls_mutex = SDL_CreateMutex(); + if (!tls_mutex) { + SDL_AtomicUnlock(&tls_lock); + return NULL; + } + } + SDL_AtomicUnlock(&tls_lock); + } + + SDL_LockMutex(tls_mutex); + for (entry = thread_local_storage; entry; entry = entry->next) { + if (entry->thread == thread) { + data = entry->data; + break; + } + } + SDL_UnlockMutex(tls_mutex); + + return data; +} + +static int SetTLSData(SDL_TLSData *data) +{ + SDL_threadID thread = SDL_ThreadID(); + SDL_TLSEntry *entry; + + /* GetTLSData() is always called first, so we can assume tls_mutex */ + SDL_LockMutex(tls_mutex); + for (entry = thread_local_storage; entry; entry = entry->next) { + if (entry->thread == thread) { + entry->data = data; + break; + } + } + if (!entry) { + entry = (SDL_TLSEntry *)SDL_malloc(sizeof(*entry)); + if (entry) { + entry->thread = thread; + entry->data = data; + entry->next = thread_local_storage; + thread_local_storage = entry; + } + } + SDL_UnlockMutex(tls_mutex); + + if (!entry) { + return SDL_OutOfMemory(); + } + return 0; +} + + +SDL_TLSID +SDL_TLSCreate() +{ + return SDL_AtomicIncRef(&tls_id)+1; +} + +void * +SDL_TLSGet(SDL_TLSID id) +{ + SDL_TLSData *data; + + data = GetTLSData(); + if (!data || id <= 0 || id > data->limit) { + return NULL; + } + return data->data[id-1]; +} + +int +SDL_TLSSet(SDL_TLSID id, const void *value) +{ + SDL_TLSData *data; + + if (id <= 0) { + return SDL_InvalidParamError(id); + } + + data = GetTLSData(); + if (!data || id > data->limit) { + int i, oldlimit, newlimit; + + oldlimit = data ? data->limit : 0; + newlimit = (id + TLS_ALLOC_CHUNKSIZE); + data = (SDL_TLSData *)SDL_realloc(data, sizeof(*data)+(newlimit-1)*sizeof(void*)); + if (!data) { + return SDL_OutOfMemory(); + } + data->limit = newlimit; + for (i = oldlimit; i < newlimit; ++i) { + data->data[i] = NULL; + } + if (SetTLSData(data) != 0) { + return -1; + } + } + + data->data[id-1] = SDL_const_cast(void*, value); + return 0; +} + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/thread/pthread/SDL_systls.c b/src/thread/pthread/SDL_systls.c new file mode 100644 index 000000000..5cbaa1b77 --- /dev/null +++ b/src/thread/pthread/SDL_systls.c @@ -0,0 +1,101 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2013 Sam Lantinga + + 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_config.h" +#include "SDL_thread.h" + +#include + + +#define TLS_ALLOC_CHUNKSIZE 8 + +typedef struct { + int limit; + void *data[1]; +} SDL_TLSData; + +static SDL_SpinLock tls_lock; +static pthread_key_t thread_local_storage; +static SDL_atomic_t tls_id; + + +SDL_TLSID +SDL_TLSCreate() +{ + if (!thread_local_storage) { + SDL_AtomicLock(&tls_lock); + if (!thread_local_storage) { + if (pthread_key_create(&thread_local_storage, NULL) != 0) { + SDL_SetError("pthread_key_create() failed"); + SDL_AtomicUnlock(&tls_lock); + return 0; + } + } + SDL_AtomicUnlock(&tls_lock); + } + return SDL_AtomicIncRef(&tls_id)+1; +} + +void * +SDL_TLSGet(SDL_TLSID id) +{ + SDL_TLSData *data; + + data = (SDL_TLSData *)pthread_getspecific(thread_local_storage); + if (!data || id <= 0 || id > data->limit) { + return NULL; + } + return data->data[id-1]; +} + +int +SDL_TLSSet(SDL_TLSID id, const void *value) +{ + SDL_TLSData *data; + + if (!thread_local_storage || id <= 0) { + return SDL_InvalidParamError(id); + } + + data = (SDL_TLSData *)pthread_getspecific(thread_local_storage); + if (!data || id > data->limit) { + int i, oldlimit, newlimit; + + oldlimit = data ? data->limit : 0; + newlimit = (id + TLS_ALLOC_CHUNKSIZE); + data = (SDL_TLSData *)SDL_realloc(data, sizeof(*data)+(newlimit-1)*sizeof(void*)); + if (!data) { + return SDL_OutOfMemory(); + } + data->limit = newlimit; + for (i = oldlimit; i < newlimit; ++i) { + data->data[i] = NULL; + } + if (pthread_setspecific(thread_local_storage, data) != 0) { + return SDL_SetError("pthread_setspecific() failed"); + } + } + + data->data[id-1] = SDL_const_cast(void*, value); + return 0; +} + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/thread/windows/SDL_systls.c b/src/thread/windows/SDL_systls.c new file mode 100644 index 000000000..2b7b39488 --- /dev/null +++ b/src/thread/windows/SDL_systls.c @@ -0,0 +1,106 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2013 Sam Lantinga + + 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_config.h" +#include "SDL_thread.h" + +#if SDL_THREAD_WINDOWS + +#include "../../core/windows/SDL_windows.h" + + +#define TLS_ALLOC_CHUNKSIZE 8 + +typedef struct { + int limit; + void *data[1]; +} SDL_TLSData; + +static SDL_SpinLock tls_lock; +static DWORD thread_local_storage = TLS_OUT_OF_INDEXES; +static SDL_atomic_t tls_id; + + +SDL_TLSID +SDL_TLSCreate() +{ + if (thread_local_storage == TLS_OUT_OF_INDEXES) { + SDL_AtomicLock(&tls_lock); + if (thread_local_storage == TLS_OUT_OF_INDEXES) { + thread_local_storage = TlsAlloc(); + if (thread_local_storage == TLS_OUT_OF_INDEXES) { + SDL_SetError("TlsAlloc() failed"); + SDL_AtomicUnlock(&tls_lock); + return 0; + } + } + SDL_AtomicUnlock(&tls_lock); + } + return SDL_AtomicIncRef(&tls_id)+1; +} + +void * +SDL_TLSGet(SDL_TLSID id) +{ + SDL_TLSData *data; + + data = (SDL_TLSData *)TlsGetValue(thread_local_storage); + if (!data || id <= 0 || id > data->limit) { + return NULL; + } + return data->data[id-1]; +} + +int +SDL_TLSSet(SDL_TLSID id, const void *value) +{ + SDL_TLSData *data; + + if (thread_local_storage == TLS_OUT_OF_INDEXES || id <= 0) { + return SDL_InvalidParamError(id); + } + + data = (SDL_TLSData *)TlsGetValue(thread_local_storage); + if (!data || id > data->limit) { + int i, oldlimit, newlimit; + + oldlimit = data ? data->limit : 0; + newlimit = (id + TLS_ALLOC_CHUNKSIZE); + data = (SDL_TLSData *)SDL_realloc(data, sizeof(*data)+(newlimit-1)*sizeof(void*)); + if (!data) { + return SDL_OutOfMemory(); + } + data->limit = newlimit; + for (i = oldlimit; i < newlimit; ++i) { + data->data[i] = NULL; + } + if (!TlsSetValue(thread_local_storage, data)) { + return SDL_SetError("TlsSetValue() failed"); + } + } + + data->data[id-1] = SDL_const_cast(void*, value); + return 0; +} + +#endif /* SDL_THREAD_WINDOWS */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/test/testthread.c b/test/testthread.c index 21509ff1d..b8f18dc06 100644 --- a/test/testthread.c +++ b/test/testthread.c @@ -19,6 +19,7 @@ #include "SDL.h" #include "SDL_thread.h" +static SDL_TLSID tls; static int alive = 0; /* Call this instead of exit(), so we can clean up SDL: atexit() is evil. */ @@ -32,8 +33,9 @@ quit(int rc) int SDLCALL ThreadFunc(void *data) { - printf("Started thread %s: My thread id is %lu\n", - (char *) data, SDL_ThreadID()); + SDL_TLSSet(tls, "baby thread"); + printf("Started thread %s: My thread id is %lu, thread data = %s\n", + (char *) data, SDL_ThreadID(), (const char *)SDL_TLSGet(tls)); while (alive) { printf("Thread '%s' is alive!\n", (char *) data); SDL_Delay(1 * 1000); @@ -62,6 +64,11 @@ main(int argc, char *argv[]) return (1); } + tls = SDL_TLSCreate(); + SDL_assert(tls); + SDL_TLSSet(tls, "main thread"); + printf("Main thread data initially: %s\n", (const char *)SDL_TLSGet(tls)); + alive = 1; thread = SDL_CreateThread(ThreadFunc, "One", "#1"); if (thread == NULL) { @@ -73,6 +80,8 @@ main(int argc, char *argv[]) alive = 0; SDL_WaitThread(thread, NULL); + printf("Main thread data finally: %s\n", (const char *)SDL_TLSGet(tls)); + alive = 1; signal(SIGTERM, killed); thread = SDL_CreateThread(ThreadFunc, "Two", "#2");