Implemented an API for thread-local storage: SDL_TLSCreate(), SDL_TLSSet(), SDL_TLSGet()
authorSam Lantinga <slouken@libsdl.org>
Wed, 10 Jul 2013 02:32:04 -0700
changeset 7391 a29895dc5e9a
parent 7390 e4b98404baa4
child 7392 7e32fcb41b44
Implemented an API for thread-local storage: SDL_TLSCreate(), SDL_TLSSet(), SDL_TLSGet()
VisualC/SDL/SDL_VS2008.vcproj
VisualC/SDL/SDL_VS2010.vcxproj
VisualC/SDL/SDL_VS2012.vcxproj
Xcode-iOS/SDL/SDL.xcodeproj/project.pbxproj
Xcode/SDL/SDL.xcodeproj/project.pbxproj
configure
configure.in
include/SDL_thread.h
src/thread/SDL_thread.c
src/thread/beos/SDL_systls.c
src/thread/generic/SDL_systls.c
src/thread/pthread/SDL_systls.c
src/thread/windows/SDL_systls.c
test/testthread.c
--- a/VisualC/SDL/SDL_VS2008.vcproj	Tue Jul 09 12:58:54 2013 -0700
+++ b/VisualC/SDL/SDL_VS2008.vcproj	Wed Jul 10 02:32:04 2013 -0700
@@ -1133,6 +1133,10 @@
 			>
 		</File>
 		<File
+			RelativePath="..\..\src\thread\windows\SDL_systls.c"
+			>
+		</File>
+		<File
 			RelativePath="..\..\src\thread\SDL_systhread.h"
 			>
 		</File>
--- a/VisualC/SDL/SDL_VS2010.vcxproj	Tue Jul 09 12:58:54 2013 -0700
+++ b/VisualC/SDL/SDL_VS2010.vcxproj	Wed Jul 10 02:32:04 2013 -0700
@@ -438,6 +438,7 @@
     <ClCompile Include="..\..\src\power\windows\SDL_syspower.c" />
     <ClCompile Include="..\..\src\thread\windows\SDL_syssem.c" />
     <ClCompile Include="..\..\src\thread\windows\SDL_systhread.c" />
+    <ClCompile Include="..\..\src\thread\windows\SDL_systls.c" />
     <ClCompile Include="..\..\src\timer\windows\SDL_systimer.c" />
     <ClCompile Include="..\..\src\thread\SDL_thread.c" />
     <ClCompile Include="..\..\src\timer\SDL_timer.c" />
@@ -462,4 +463,4 @@
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">
   </ImportGroup>
-</Project>
\ No newline at end of file
+</Project>
--- a/VisualC/SDL/SDL_VS2012.vcxproj	Tue Jul 09 12:58:54 2013 -0700
+++ b/VisualC/SDL/SDL_VS2012.vcxproj	Wed Jul 10 02:32:04 2013 -0700
@@ -441,6 +441,7 @@
     <ClCompile Include="..\..\src\power\windows\SDL_syspower.c" />
     <ClCompile Include="..\..\src\thread\windows\SDL_syssem.c" />
     <ClCompile Include="..\..\src\thread\windows\SDL_systhread.c" />
+    <ClCompile Include="..\..\src\thread\windows\SDL_systls.c" />
     <ClCompile Include="..\..\src\timer\windows\SDL_systimer.c" />
     <ClCompile Include="..\..\src\thread\SDL_thread.c" />
     <ClCompile Include="..\..\src\timer\SDL_timer.c" />
@@ -466,4 +467,4 @@
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">
   </ImportGroup>
-</Project>
\ No newline at end of file
+</Project>
--- a/Xcode-iOS/SDL/SDL.xcodeproj/project.pbxproj	Tue Jul 09 12:58:54 2013 -0700
+++ b/Xcode-iOS/SDL/SDL.xcodeproj/project.pbxproj	Wed Jul 10 02:32:04 2013 -0700
@@ -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 = "<group>"; };
 		AA0AD06116647BBB00CE5896 /* SDL_gamecontroller.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_gamecontroller.c; sourceTree = "<group>"; };
 		AA0AD06416647BD400CE5896 /* SDL_gamecontroller.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_gamecontroller.h; sourceTree = "<group>"; };
+		AA0F8494178D5F1A00823F9D /* SDL_systls.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_systls.c; sourceTree = "<group>"; };
 		AA126AD21617C5E6005ABC8F /* SDL_uikitmodes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_uikitmodes.h; sourceTree = "<group>"; };
 		AA126AD31617C5E6005ABC8F /* SDL_uikitmodes.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDL_uikitmodes.m; sourceTree = "<group>"; };
 		AA628AD9159369E3005138DD /* SDL_rotate.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_rotate.c; sourceTree = "<group>"; };
@@ -851,6 +853,7 @@
 				FD99BA0A0DD52EDC00FB1D6B /* SDL_syssem.c */,
 				FD99BA0B0DD52EDC00FB1D6B /* SDL_systhread.c */,
 				FD99BA0C0DD52EDC00FB1D6B /* SDL_systhread_c.h */,
+				AA0F8494178D5F1A00823F9D /* SDL_systls.c */,
 			);
 			path = pthread;
 			sourceTree = "<group>";
@@ -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;
 		};
--- a/Xcode/SDL/SDL.xcodeproj/project.pbxproj	Tue Jul 09 12:58:54 2013 -0700
+++ b/Xcode/SDL/SDL.xcodeproj/project.pbxproj	Wed Jul 10 02:32:04 2013 -0700
@@ -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 = "<group>"; };
 		566CDE8E148F0AC200C5A9BB /* SDL_dropevents.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_dropevents.c; sourceTree = "<group>"; };
 		A77E6EB3167AB0A90010E40B /* SDL_gamecontroller.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_gamecontroller.h; sourceTree = "<group>"; };
+		AA0F8490178D5ECC00823F9D /* SDL_systls.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_systls.c; sourceTree = "<group>"; };
 		AA628AC8159367B7005138DD /* SDL_rotate.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_rotate.c; sourceTree = "<group>"; };
 		AA628AC9159367B7005138DD /* SDL_rotate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_rotate.h; sourceTree = "<group>"; };
 		AA628ACF159367F2005138DD /* SDL_x11xinput2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_x11xinput2.c; sourceTree = "<group>"; };
@@ -1462,6 +1466,7 @@
 				04BDFE8112E6671800899322 /* SDL_syssem.c */,
 				04BDFE8212E6671800899322 /* SDL_systhread.c */,
 				04BDFE8312E6671800899322 /* SDL_systhread_c.h */,
+				AA0F8490178D5ECC00823F9D /* SDL_systls.c */,
 			);
 			path = pthread;
 			sourceTree = "<group>";
@@ -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;
 		};
--- a/configure	Tue Jul 09 12:58:54 2013 -0700
+++ b/configure	Wed Jul 10 02:32:04 2013 -0700
@@ -1511,7 +1511,7 @@
   --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,8 +17535,20 @@
 if test "${enable_oss+set}" = set; then :
   enableval=$enable_oss;
 else
-  enable_oss=yes
-fi
+  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
@@ -21423,6 +21435,9 @@
             # 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 @@
                 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_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
--- a/configure.in	Tue Jul 09 12:58:54 2013 -0700
+++ b/configure.in	Wed Jul 10 02:32:04 2013 -0700
@@ -657,6 +657,7 @@
         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 @@
             # 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 @@
         # 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
--- a/include/SDL_thread.h	Tue Jul 09 12:58:54 2013 -0700
+++ b/include/SDL_thread.h	Wed Jul 10 02:32:04 2013 -0700
@@ -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 @@
 /* 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 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
--- a/src/thread/SDL_thread.c	Tue Jul 09 12:58:54 2013 -0700
+++ b/src/thread/SDL_thread.c	Wed Jul 10 02:32:04 2013 -0700
@@ -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 @@
         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_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 @@
         if (status) {
             *status = thread->status;
         }
-        SDL_DelThread(thread);
         SDL_free(thread->name);
         SDL_free(thread);
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/thread/beos/SDL_systls.c	Wed Jul 10 02:32:04 2013 -0700
@@ -0,0 +1,106 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2013 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_config.h"
+#include "SDL_thread.h"
+
+#if SDL_THREAD_BEOS
+
+#include <support/TLS.h>
+
+
+#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: */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/thread/generic/SDL_systls.c	Wed Jul 10 02:32:04 2013 -0700
@@ -0,0 +1,163 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2013 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_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: */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/thread/pthread/SDL_systls.c	Wed Jul 10 02:32:04 2013 -0700
@@ -0,0 +1,101 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2013 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_config.h"
+#include "SDL_thread.h"
+
+#include <pthread.h>
+
+
+#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: */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/thread/windows/SDL_systls.c	Wed Jul 10 02:32:04 2013 -0700
@@ -0,0 +1,106 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2013 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_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: */
--- a/test/testthread.c	Tue Jul 09 12:58:54 2013 -0700
+++ b/test/testthread.c	Wed Jul 10 02:32:04 2013 -0700
@@ -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 @@
 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 @@
         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 @@
     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");