SDL: thread: Reworked SDL_CreateThread to be consistent across platforms.

From 0ec716819e44b89350bdd52d34cec9955780be2d Mon Sep 17 00:00:00 2001
From: "Ryan C. Gordon" <[EMAIL REDACTED]>
Date: Tue, 21 May 2024 01:46:48 -0400
Subject: [PATCH] thread: Reworked SDL_CreateThread to be consistent across
 platforms.

Also documented missing and weird bits, rename typedefs to fit SDL standards.
---
 docs/README-migration.md                   |   2 +
 include/SDL3/SDL_thread.h                  | 211 +++++++++++++--------
 src/audio/SDL_audio.c                      |   3 +-
 src/audio/coreaudio/SDL_coreaudio.m        |   2 +-
 src/audio/pulseaudio/SDL_pulseaudio.c      |   2 +-
 src/audio/wasapi/SDL_wasapi.c              |   2 +-
 src/camera/SDL_camera.c                    |   2 +-
 src/core/haiku/SDL_BeApp.cc                |   2 +-
 src/dialog/windows/SDL_windowsdialog.c     |   6 +-
 src/dynapi/SDL_dynapi.sym                  |   4 +-
 src/dynapi/SDL_dynapi_overrides.h          |   4 +-
 src/dynapi/SDL_dynapi_procs.h              |  22 +--
 src/hidapi/libusb/hidapi_thread_sdl.h      |   2 +-
 src/joystick/hidapi/SDL_hidapi_rumble.c    |   2 +-
 src/joystick/windows/SDL_windowsjoystick.c |   2 +-
 src/sensor/android/SDL_androidsensor.c     |   2 +-
 src/thread/SDL_systhread.h                 |  13 +-
 src/thread/SDL_thread.c                    |  59 ++----
 src/thread/generic/SDL_systhread.c         |   8 +-
 src/thread/n3ds/SDL_systhread.c            |   7 +-
 src/thread/ngage/SDL_systhread.cpp         |   4 +-
 src/thread/ps2/SDL_systhread.c             |   4 +-
 src/thread/psp/SDL_systhread.c             |   4 +-
 src/thread/pthread/SDL_systhread.c         |   4 +-
 src/thread/stdcpp/SDL_systhread.cpp        |   4 +-
 src/thread/vita/SDL_systhread.c            |   5 +-
 src/thread/windows/SDL_systhread.c         |  35 +---
 src/timer/SDL_timer.c                      |   2 +-
 src/video/psp/SDL_pspevents.c              |   2 +-
 src/video/winrt/SDL_winrtevents.cpp        |   2 +-
 30 files changed, 209 insertions(+), 214 deletions(-)

diff --git a/docs/README-migration.md b/docs/README-migration.md
index c0794c98d054e..f8d33a0d235bc 100644
--- a/docs/README-migration.md
+++ b/docs/README-migration.md
@@ -1630,6 +1630,8 @@ becomes:
 
 ## SDL_thread.h
 
+SDL_CreateThread and SDL_CreateThreadWithStackSpace now take beginthread/endthread function pointers on all platforms (ignoring them on most), and have been replaced with macros that hide this detail on all platforms. This works the same as before at the source code level, but the actual function signature that is called in SDL has changed. The library's exported symbol is SDL_CreateThreadRuntime, and looking for "SDL_CreateThread" in the DLL/Shared Library/Dylib will fail. You should not call this directly, but instead always use the macro!
+
 The following functions have been renamed:
 * SDL_TLSCleanup() => SDL_CleanupTLS()
 * SDL_TLSCreate() => SDL_CreateTLS()
diff --git a/include/SDL3/SDL_thread.h b/include/SDL3/SDL_thread.h
index 2f231157598ec..e830470569cf3 100644
--- a/include/SDL3/SDL_thread.h
+++ b/include/SDL3/SDL_thread.h
@@ -45,14 +45,44 @@
 extern "C" {
 #endif
 
-/* The SDL thread structure, defined in SDL_thread.c */
-struct SDL_Thread;
+/**
+ * The SDL thread object.
+ *
+ * These are opaque data.
+ *
+ * \since This datatype is available since SDL 3.0.0.
+ *
+ * \sa SDL_CreateThread
+ * \sa SDL_WaitThread
+ */
 typedef struct SDL_Thread SDL_Thread;
 
-/* The SDL thread ID */
+/**
+ * A unique numeric ID that identifies a thread.
+ *
+ * These are different that SDL_Thread objects, which are generally what an
+ * application will operate on, but having a way to uniquely identify a
+ * thread can be useful at times.
+ *
+ * \since This datatype is available since SDL 3.0.0.
+ *
+ * \sa SDL_GetThreadID
+ * \sa SDL_GetCurrentThreadID
+ */
 typedef Uint64 SDL_ThreadID;
 
-/* Thread local storage ID, 0 is the invalid ID */
+/**
+ * Thread local storage ID values.
+ *
+ * 0 is the invalid ID. An app can create these and then set data for
+ * these IDs that is unique to each thread.
+ *
+ * \since This datatype is available since SDL 3.0.0.
+ *
+ * \sa SDL_CreateTLS
+ * \sa SDL_GetTLS
+ * \sa SDL_SetTLS
+ */
 typedef Uint32 SDL_TLSID;
 
 /**
@@ -74,7 +104,7 @@ typedef enum SDL_ThreadPriority {
 } SDL_ThreadPriority;
 
 /**
- * The function passed to SDL_CreateThread().
+ * The function passed to SDL_CreateThread() as the new thread's entry point.
  *
  * \param data what was passed as `data` to SDL_CreateThread()
  * \returns a value that can be reported through SDL_WaitThread().
@@ -83,91 +113,86 @@ typedef enum SDL_ThreadPriority {
  */
 typedef int (SDLCALL * SDL_ThreadFunction) (void *data);
 
-/*
- *  We compile SDL into a DLL. This means, that it's the DLL which
- *  creates a new thread for the calling process with the SDL_CreateThread()
- *  API. There is a problem with this, that only the RTL of the SDL3.DLL will
- *  be initialized for those threads, and not the RTL of the calling
- *  application!
- *
- *  To solve this, we make a little hack here.
- *
- *  We'll always use the caller's _beginthread() and _endthread() APIs to
- *  start a new thread. This way, if it's the SDL3.DLL which uses this API,
- *  then the RTL of SDL3.DLL will be used to create the new thread, and if it's
- *  the application, then the RTL of the application will be used.
- *
- *  So, in short:
- *  Always use the _beginthread() and _endthread() of the calling runtime
- *  library!
- */
-#if (defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_GDK)) && !defined(SDL_PLATFORM_WINRT)
-#define SDL_PASSED_BEGINTHREAD_ENDTHREAD
-
-typedef uintptr_t (__cdecl * pfnSDL_CurrentBeginThread)
-                   (void *, unsigned, unsigned (__stdcall *func)(void *),
-                    void * /*arg*/, unsigned, unsigned * /* threadID */);
-typedef void (__cdecl * pfnSDL_CurrentEndThread) (unsigned code);
-
-#ifndef SDL_beginthread
-#define SDL_beginthread _beginthreadex
-#endif
-#ifndef SDL_endthread
-#define SDL_endthread _endthreadex
-#endif
 
+#if (defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_GDK)) && !defined(SDL_PLATFORM_WINRT)
 
-/*
- * Create a SDL Thread
+#ifndef SDL_BeginThreadFunction
+/**
+ * Macro that manages the compiler's `_beginthreadex` implementation.
  *
- * \param fn Thread function
- * \param name name
- * \param data some data
- * \param pfnBeginThread begin function
- * \param pfnEndThread end function
+ * On Windows (and maybe other platforms), a program might use a different
+ * C runtime than its libraries. Or, in SDL's case, it might use a C runtime
+ * while SDL uses none at all.
  *
- * \returns SDL_Thread pointer
+ * C runtimes expect to initialize thread-specific details when a new thread
+ * is created, but to do this in SDL_CreateThread would require SDL to know
+ * intimate details about the caller's C runtime, which is not possible.
  *
- * \since This function is available since SDL 3.0.0.
+ * So SDL_CreateThread has two extra parameters, which are
+ * hidden at compile time by macros: the C runtime's `_beginthreadex` and
+ * `_endthreadex` entry points. If these are not NULL, they are used to spin
+ * and terminate the new thread; otherwise the standard Win32 `CreateThread`
+ * function is used. When `SDL_CreateThread` is called from a compiler that
+ * needs this C runtime thread init function, macros insert the appropriate
+ * function pointers for SDL_CreateThread's caller (which might be a different
+ * compiler with a different runtime in different calls to SDL_CreateThread!).
+ *
+ * This defaults to `_beginthreadex` on Windows (and NULL everywhere else),
+ * but apps that have extremely specific special needs can define this to
+ * something else and the SDL headers will use it, passing the app-defined
+ * value to SDL_CreateThread calls. Redefine this with caution!
+ *
+ * Unless you are doing something extremely complicated, like perhaps a
+ * language binding, **you should never reference this directly**. Let SDL's
+ * macros handle this platform-specific detail transparently!
+ *
+ * \threadsafety It is safe to call this macro from any thread.
+ *
+ * \since This macro is available since SDL 3.0.0.
+ *
+ * \sa SDL_CreateThread
  */
-extern SDL_DECLSPEC SDL_Thread *SDLCALL
-SDL_CreateThread(SDL_ThreadFunction fn, const char *name, void *data,
-                 pfnSDL_CurrentBeginThread pfnBeginThread,
-                 pfnSDL_CurrentEndThread pfnEndThread);
+#define SDL_BeginThreadFunction _beginthreadex
+#endif
 
-/*
- * Create a SDL Thread, with explicit stack size
+#ifndef SDL_EndThreadFunction
+/**
+ * Macro that manages the compiler's `_endthreadex` implementation.
  *
- * \param fn Thread function
- * \param name name
- * \param stacksize stack size
- * \param data some data
- * \param pfnBeginThread begin function
- * \param pfnEndThread end function
+ * Please see the detailed explanation in SDL_BeginThreadFunction.
  *
- * \returns SDL_Thread pointer
+ * This defaults to `_endthreadex` on Windows (and NULL everywhere else),
+ * but apps that have extremely specific special needs can define this to
+ * something else and the SDL headers will use it, passing the app-defined
+ * value to SDL_CreateThread calls. Redefine this with caution!
  *
- * \since This function is available since SDL 3.0.0.
+ * Unless you are doing something extremely complicated, like perhaps a
+ * language binding, **you should never reference this directly**. Let SDL's
+ * macros handle this platform-specific detail transparently!
+ *
+ * \threadsafety It is safe to call this macro from any thread.
+ *
+ * \since This macro is available since SDL 3.0.0.
+ *
+ * \sa SDL_CreateThread
  */
-extern SDL_DECLSPEC SDL_Thread *SDLCALL
-SDL_CreateThreadWithStackSize(SDL_ThreadFunction fn,
-                 const char *name, const size_t stacksize, void *data,
-                 pfnSDL_CurrentBeginThread pfnBeginThread,
-                 pfnSDL_CurrentEndThread pfnEndThread);
-
-#if !defined(__BUILDING_SDL2_COMPAT__) /* do not conflict with sdl2-compat::sdl3_include_wrapper.h */
-#if defined(SDL_CreateThread) && SDL_DYNAMIC_API
-#undef SDL_CreateThread
-#define SDL_CreateThread(fn, name, data) SDL_CreateThread_REAL(fn, name, data, (pfnSDL_CurrentBeginThread)SDL_beginthread, (pfnSDL_CurrentEndThread)SDL_endthread)
-#undef SDL_CreateThreadWithStackSize
-#define SDL_CreateThreadWithStackSize(fn, name, stacksize, data) SDL_CreateThreadWithStackSize_REAL(fn, name, stacksize, data, (pfnSDL_CurrentBeginThread)SDL_beginthread, (pfnSDL_CurrentEndThread)SDL_endthread)
-#else
-#define SDL_CreateThread(fn, name, data) SDL_CreateThread(fn, name, data, (pfnSDL_CurrentBeginThread)SDL_beginthread, (pfnSDL_CurrentEndThread)SDL_endthread)
-#define SDL_CreateThreadWithStackSize(fn, name, stacksize, data) SDL_CreateThreadWithStackSize(fn, name, stacksize, data, (pfnSDL_CurrentBeginThread)SDL_beginthread, (pfnSDL_CurrentEndThread)SDL_endthread)
+#define SDL_EndThreadFunction _endthreadex
+#endif
+#endif
+
+/* currently no other platforms than Windows use _beginthreadex/_endthreadex things. */
+#ifndef SDL_WIKI_DOCUMENTATION_SECTION
+#ifndef SDL_BeginThreadFunction
+#define SDL_BeginThreadFunction NULL
+#endif
+#ifndef SDL_EndThreadFunction
+#define SDL_EndThreadFunction NULL
+#endif
 #endif
-#endif /* !__BUILDING_SDL2_COMPAT__ */
 
-#else
+#ifdef SDL_WIKI_DOCUMENTATION_SECTION
+
+/* Note that this isn't the correct function signature, but this is what the API reference manual should look like for all intents and purposes. */
 
 /**
  * Create a new thread with a default stack size.
@@ -178,6 +203,16 @@ SDL_CreateThreadWithStackSize(SDL_ThreadFunction fn,
  * SDL_CreateThreadWithStackSize(fn, name, 0, data);
  * ```
  *
+ * Note that this "function" is actually a macro that calls an internal
+ * function with two extra parameters not listed here; they are
+ * hidden through preprocessor macros and are needed to support various C
+ * runtimes at the point of the function call. Language bindings that aren't
+ * using the C headers will need to deal with this.
+ *
+ * Usually, apps should just call this function the same way on every platform and
+ * let the macros hide the details. See SDL_BeginThreadFunction for the
+ * technical details.
+ *
  * \param fn the SDL_ThreadFunction function to call in the new thread
  * \param name the name of the thread
  * \param data a pointer that is passed to `fn`
@@ -219,6 +254,21 @@ extern SDL_DECLSPEC SDL_Thread * SDLCALL SDL_CreateThread(SDL_ThreadFunction fn,
  * multiple of the system's page size (in many cases, this is 4 kilobytes, but
  * check your system documentation).
  *
+ * Note that this "function" is actually a macro that calls an internal
+ * function with two extra parameters not listed here; they are
+ * hidden through preprocessor macros and are needed to support various C
+ * runtimes at the point of the function call. Language bindings that aren't
+ * using the C headers will need to deal with this.
+ *
+ * The actual symbol in SDL's library is `SDL_CreateThreadRuntime` (or
+ * `SDL_CreateThreadWithStackSpaceRuntime`), so there is no symbol clash, but
+ * trying to load an SDL shared library and look for "SDL_CreateThread"
+ * will fail.
+ *
+ * Usually, apps should just call this function the same way on every platform and
+ * let the macros hide the details. See SDL_BeginThreadFunction for the
+ * technical details.
+ *
  * \param fn the SDL_ThreadFunction function to call in the new thread
  * \param name the name of the thread
  * \param stacksize the size, in bytes, to allocate for the new thread stack.
@@ -233,7 +283,14 @@ extern SDL_DECLSPEC SDL_Thread * SDLCALL SDL_CreateThread(SDL_ThreadFunction fn,
  * \sa SDL_WaitThread
  */
 extern SDL_DECLSPEC SDL_Thread * SDLCALL SDL_CreateThreadWithStackSize(SDL_ThreadFunction fn, const char *name, const size_t stacksize, void *data);
+#endif
 
+#ifndef SDL_WIKI_DOCUMENTATION_SECTION
+/* These are the actual functions exported from SDL! Don't use them directly! Use the SDL_CreateThread and SDL_CreateThreadWithStackSize macros! */
+extern SDL_DECLSPEC SDL_Thread *SDLCALL SDL_CreateThreadRuntime(SDL_ThreadFunction fn, const char *name, void *data, SDL_FunctionPointer pfnBeginThread, SDL_FunctionPointer pfnEndThread);
+extern SDL_DECLSPEC SDL_Thread *SDLCALL SDL_CreateThreadWithStackSizeRuntime(SDL_ThreadFunction fn, const char *name, const size_t stacksize, void *data,SDL_FunctionPointer pfnBeginThread, SDL_FunctionPointer pfnEndThread);
+#define SDL_CreateThread(fn, name, data) SDL_CreateThreadRuntime(fn, name, data, (SDL_FunctionPointer) (SDL_BeginThreadFunction), (SDL_FunctionPointer) (SDL_EndThreadFunction))
+#define SDL_CreateThreadWithStackSize(fn, name, stacksize, data) SDL_CreateThreadWithStackSizeRuntime(fn, name, stacksize, data, (SDL_FunctionPointer) (SDL_BeginThreadFunction), (SDL_FunctionPointer) (SDL_EndThreadFunction))
 #endif
 
 /**
diff --git a/src/audio/SDL_audio.c b/src/audio/SDL_audio.c
index d86cf133b59da..c6645943ef2aa 100644
--- a/src/audio/SDL_audio.c
+++ b/src/audio/SDL_audio.c
@@ -1633,10 +1633,9 @@ static int OpenPhysicalAudioDevice(SDL_AudioDevice *device, const SDL_AudioSpec
 
     // Start the audio thread if necessary
     if (!current_audio.impl.ProvidesOwnCallbackThread) {
-        const size_t stacksize = 0;  // just take the system default, since audio streams might have callbacks.
         char threadname[64];
         SDL_GetAudioThreadName(device, threadname, sizeof (threadname));
-        device->thread = SDL_CreateThreadInternal(device->iscapture ? CaptureAudioThread : OutputAudioThread, threadname, stacksize, device);
+        device->thread = SDL_CreateThread(device->iscapture ? CaptureAudioThread : OutputAudioThread, threadname, device);
 
         if (!device->thread) {
             ClosePhysicalAudioDevice(device);
diff --git a/src/audio/coreaudio/SDL_coreaudio.m b/src/audio/coreaudio/SDL_coreaudio.m
index 8de8d96c052b0..44cd6d9ca0977 100644
--- a/src/audio/coreaudio/SDL_coreaudio.m
+++ b/src/audio/coreaudio/SDL_coreaudio.m
@@ -952,7 +952,7 @@ static int COREAUDIO_OpenDevice(SDL_AudioDevice *device)
 
     char threadname[64];
     SDL_GetAudioThreadName(device, threadname, sizeof(threadname));
-    device->hidden->thread = SDL_CreateThreadInternal(AudioQueueThreadEntry, threadname, 0, device);
+    device->hidden->thread = SDL_CreateThread(AudioQueueThreadEntry, threadname, device);
     if (!device->hidden->thread) {
         return -1;
     }
diff --git a/src/audio/pulseaudio/SDL_pulseaudio.c b/src/audio/pulseaudio/SDL_pulseaudio.c
index 1898a19e26985..e36e36838861a 100644
--- a/src/audio/pulseaudio/SDL_pulseaudio.c
+++ b/src/audio/pulseaudio/SDL_pulseaudio.c
@@ -978,7 +978,7 @@ static void PULSEAUDIO_DetectDevices(SDL_AudioDevice **default_output, SDL_Audio
 
     // ok, we have a sane list, let's set up hotplug notifications now...
     SDL_AtomicSet(&pulseaudio_hotplug_thread_active, 1);
-    pulseaudio_hotplug_thread = SDL_CreateThreadInternal(HotplugThread, "PulseHotplug", 256 * 1024, ready_sem);  // !!! FIXME: this can probably survive in significantly less stack space.
+    pulseaudio_hotplug_thread = SDL_CreateThreadWithStackSize(HotplugThread, "PulseHotplug", 256 * 1024, ready_sem);  // !!! FIXME: this can probably survive in significantly less stack space.
     SDL_WaitSemaphore(ready_sem);
     SDL_DestroySemaphore(ready_sem);
 }
diff --git a/src/audio/wasapi/SDL_wasapi.c b/src/audio/wasapi/SDL_wasapi.c
index c7d0fb328288b..54322742d3966 100644
--- a/src/audio/wasapi/SDL_wasapi.c
+++ b/src/audio/wasapi/SDL_wasapi.c
@@ -210,7 +210,7 @@ static int InitManagementThread(void)
 
     SDL_AtomicSetPtr((void **) &ManagementThreadPendingTasks, NULL);
     SDL_AtomicSet(&ManagementThreadShutdown, 0);
-    ManagementThread = SDL_CreateThreadInternal(ManagementThreadEntry, "SDLWASAPIMgmt", 256 * 1024, &mgmtdata); // !!! FIXME: maybe even smaller stack size?
+    ManagementThread = SDL_CreateThreadWithStackSize(ManagementThreadEntry, "SDLWASAPIMgmt", 256 * 1024, &mgmtdata); // !!! FIXME: maybe even smaller stack size?
     if (!ManagementThread) {
         return -1;
     }
diff --git a/src/camera/SDL_camera.c b/src/camera/SDL_camera.c
index 081b3df408054..3b337fb896eed 100644
--- a/src/camera/SDL_camera.c
+++ b/src/camera/SDL_camera.c
@@ -1150,7 +1150,7 @@ SDL_Camera *SDL_OpenCameraDevice(SDL_CameraDeviceID instance_id, const SDL_Camer
     if (!camera_driver.impl.ProvidesOwnCallbackThread) {
         char threadname[64];
         SDL_GetCameraThreadName(device, threadname, sizeof (threadname));
-        device->thread = SDL_CreateThreadInternal(CameraThread, threadname, 0, device);
+        device->thread = SDL_CreateThread(CameraThread, threadname, device);
         if (!device->thread) {
             ClosePhysicalCameraDevice(device);
             ReleaseCameraDevice(device);
diff --git a/src/core/haiku/SDL_BeApp.cc b/src/core/haiku/SDL_BeApp.cc
index 68a79f4e306f1..ba0f256e3ce2a 100644
--- a/src/core/haiku/SDL_BeApp.cc
+++ b/src/core/haiku/SDL_BeApp.cc
@@ -107,7 +107,7 @@ static int StartBeApp(void *unused)
 static int StartBeLooper()
 {
     if (!be_app) {
-        SDL_AppThread = SDL_CreateThreadInternal(StartBeApp, "SDLApplication", 0, NULL);
+        SDL_AppThread = SDL_CreateThread(StartBeApp, "SDLApplication", NULL);
         if (!SDL_AppThread) {
             return SDL_SetError("Couldn't create BApplication thread");
         }
diff --git a/src/dialog/windows/SDL_windowsdialog.c b/src/dialog/windows/SDL_windowsdialog.c
index f02792e763b40..d749c711bfa91 100644
--- a/src/dialog/windows/SDL_windowsdialog.c
+++ b/src/dialog/windows/SDL_windowsdialog.c
@@ -459,7 +459,7 @@ void SDL_ShowOpenFileDialog(SDL_DialogFileCallback callback, void* userdata, SDL
     args->callback = callback;
     args->userdata = userdata;
 
-    thread = SDL_CreateThreadInternal(windows_file_dialog_thread, "SDL_ShowOpenFileDialog", 0, (void *) args);
+    thread = SDL_CreateThread(windows_file_dialog_thread, "SDL_ShowOpenFileDialog", (void *) args);
 
     if (thread == NULL) {
         callback(userdata, NULL, -1);
@@ -495,7 +495,7 @@ void SDL_ShowSaveFileDialog(SDL_DialogFileCallback callback, void* userdata, SDL
     args->callback = callback;
     args->userdata = userdata;
 
-    thread = SDL_CreateThreadInternal(windows_file_dialog_thread, "SDL_ShowSaveFileDialog", 0, (void *) args);
+    thread = SDL_CreateThread(windows_file_dialog_thread, "SDL_ShowSaveFileDialog", (void *) args);
 
     if (thread == NULL) {
         callback(userdata, NULL, -1);
@@ -528,7 +528,7 @@ void SDL_ShowOpenFolderDialog(SDL_DialogFileCallback callback, void* userdata, S
     args->default_folder = default_location;
     args->userdata = userdata;
 
-    thread = SDL_CreateThreadInternal(windows_folder_dialog_thread, "SDL_ShowOpenFolderDialog", 0, (void *) args);
+    thread = SDL_CreateThread(windows_folder_dialog_thread, "SDL_ShowOpenFolderDialog", (void *) args);
 
     if (thread == NULL) {
         callback(userdata, NULL, -1);
diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym
index bca7ca540585a..82c6165c31b06 100644
--- a/src/dynapi/SDL_dynapi.sym
+++ b/src/dynapi/SDL_dynapi.sym
@@ -84,8 +84,8 @@ SDL3_0.0.0 {
     SDL_CreateTexture;
     SDL_CreateTextureFromSurface;
     SDL_CreateTextureWithProperties;
-    SDL_CreateThread;
-    SDL_CreateThreadWithStackSize;
+    SDL_CreateThreadRuntime;
+    SDL_CreateThreadWithStackSizeRuntime;
     SDL_CreateWindow;
     SDL_CreateWindowAndRenderer;
     SDL_CreateWindowWithProperties;
diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h
index ae7e584dbe1ae..45b44358debb5 100644
--- a/src/dynapi/SDL_dynapi_overrides.h
+++ b/src/dynapi/SDL_dynapi_overrides.h
@@ -109,8 +109,8 @@
 #define SDL_CreateTexture SDL_CreateTexture_REAL
 #define SDL_CreateTextureFromSurface SDL_CreateTextureFromSurface_REAL
 #define SDL_CreateTextureWithProperties SDL_CreateTextureWithProperties_REAL
-#define SDL_CreateThread    SDL_CreateThread_REAL
-#define SDL_CreateThreadWithStackSize   SDL_CreateThreadWithStackSize_REAL
+#define SDL_CreateThreadRuntime    SDL_CreateThreadRuntime_REAL
+#define SDL_CreateThreadWithStackSizeRuntime   SDL_CreateThreadWithStackSizeRuntime_REAL
 #define SDL_CreateWindow SDL_CreateWindow_REAL
 #define SDL_CreateWindowAndRenderer SDL_CreateWindowAndRenderer_REAL
 #define SDL_CreateWindowWithProperties SDL_CreateWindowWithProperties_REAL
diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h
index a9cd547fb7661..67a212163370d 100644
--- a/src/dynapi/SDL_dynapi_procs.h
+++ b/src/dynapi/SDL_dynapi_procs.h
@@ -46,26 +46,6 @@ SDL_DYNAPI_PROC(int,SDL_sscanf,(const char *a, SDL_SCANF_FORMAT_STRING const cha
 SDL_DYNAPI_PROC(int,SDL_swprintf,(SDL_OUT_Z_CAP(b) wchar_t *a, size_t b, SDL_PRINTF_FORMAT_STRING const wchar_t *c, ...),(a,b,c),return)
 #endif
 
-#ifdef SDL_CreateThread
-#undef SDL_CreateThread
-#endif
-
-#if defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_GDK)
-SDL_DYNAPI_PROC(SDL_Thread*,SDL_CreateThread,(SDL_ThreadFunction a, const char *b, void *c, pfnSDL_CurrentBeginThread d, pfnSDL_CurrentEndThread e),(a,b,c,d,e),return)
-#else
-SDL_DYNAPI_PROC(SDL_Thread*,SDL_CreateThread,(SDL_ThreadFunction a, const char *b, void *c),(a,b,c),return)
-#endif
-
-#ifdef SDL_CreateThreadWithStackSize
-#undef SDL_CreateThreadWithStackSize
-#endif
-
-#if defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_GDK)
-SDL_DYNAPI_PROC(SDL_Thread*,SDL_CreateThreadWithStackSize,(SDL_ThreadFunction a, const char *b, const size_t c, void *d, pfnSDL_CurrentBeginThread e, pfnSDL_CurrentEndThread f),(a,b,c,d,e,f),return)
-#else
-SDL_DYNAPI_PROC(SDL_Thread*,SDL_CreateThreadWithStackSize,(SDL_ThreadFunction a, const char *b, const size_t c, void *d),(a,b,c,d),return)
-#endif
-
 /* New API symbols are added at the end */
 SDL_DYNAPI_PROC(SDL_Surface*,SDL_AcquireCameraFrame,(SDL_Camera *a, Uint64 *b),(a,b),return)
 SDL_DYNAPI_PROC(int,SDL_AddEventWatch,(SDL_EventFilter a, void *b),(a,b),return)
@@ -149,6 +129,8 @@ SDL_DYNAPI_PROC(SDL_TLSID,SDL_CreateTLS,(void),(),return)
 SDL_DYNAPI_PROC(SDL_Texture*,SDL_CreateTexture,(SDL_Renderer *a, SDL_PixelFormatEnum b, int c, int d, int e),(a,b,c,d,e),return)
 SDL_DYNAPI_PROC(SDL_Texture*,SDL_CreateTextureFromSurface,(SDL_Renderer *a, SDL_Surface *b),(a,b),return)
 SDL_DYNAPI_PROC(SDL_Texture*,SDL_CreateTextureWithProperties,(SDL_Renderer *a, SDL_PropertiesID b),(a,b),return)
+SDL_DYNAPI_PROC(SDL_Thread*,SDL_CreateThreadRuntime,(SDL_ThreadFunction a, const char *b, void *c, SDL_FunctionPointer d, SDL_FunctionPointer e),(a,b,c,d,e),return)
+SDL_DYNAPI_PROC(SDL_Thread*,SDL_CreateThreadWithStackSizeRuntime,(SDL_ThreadFunction a, const char *b, const size_t c, void *d, SDL_FunctionPointer e, SDL_FunctionPointer f),(a,b,c,d,e,f),return)
 SDL_DYNAPI_PROC(SDL_Window*,SDL_CreateWindow,(const char *a, int b, int c, SDL_WindowFlags d),(a,b,c,d),return)
 SDL_DYNAPI_PROC(int,SDL_CreateWindowAndRenderer,(const char *a, int b, int c, SDL_WindowFlags d, SDL_Window **e, SDL_Renderer **f),(a,b,c,d,e,f),return)
 SDL_DYNAPI_PROC(SDL_Window*,SDL_CreateWindowWithProperties,(SDL_PropertiesID a),(a),return)
diff --git a/src/hidapi/libusb/hidapi_thread_sdl.h b/src/hidapi/libusb/hidapi_thread_sdl.h
index 78b7e09eb5d83..fc27721c7eef4 100644
--- a/src/hidapi/libusb/hidapi_thread_sdl.h
+++ b/src/hidapi/libusb/hidapi_thread_sdl.h
@@ -179,7 +179,7 @@ static void hidapi_thread_create(hidapi_thread_state *state, void *(*func)(void*
      */
     param->func = func;
     param->func_arg = func_arg;
-    state->thread = SDL_CreateThreadInternal(RunInputThread, "libusb", 0, param);
+    state->thread = SDL_CreateThread(RunInputThread, "libusb", param);
 }
 
 static void hidapi_thread_join(hidapi_thread_state *state)
diff --git a/src/joystick/hidapi/SDL_hidapi_rumble.c b/src/joystick/hidapi/SDL_hidapi_rumble.c
index 8dd7316c1e444..a639823637cdc 100644
--- a/src/joystick/hidapi/SDL_hidapi_rumble.c
+++ b/src/joystick/hidapi/SDL_hidapi_rumble.c
@@ -156,7 +156,7 @@ static int SDL_HIDAPI_StartRumbleThread(SDL_HIDAPI_RumbleContext *ctx)
     }
 
     SDL_AtomicSet(&ctx->running, SDL_TRUE);
-    ctx->thread = SDL_CreateThreadInternal(SDL_HIDAPI_RumbleThread, "HIDAPI Rumble", 0, ctx);
+    ctx->thread = SDL_CreateThread(SDL_HIDAPI_RumbleThread, "HIDAPI Rumble", ctx);
     if (!ctx->thread) {
         SDL_HIDAPI_StopRumbleThread(ctx);
         return -1;
diff --git a/src/joystick/windows/SDL_windowsjoystick.c b/src/joystick/windows/SDL_windowsjoystick.c
index 6bd204ae42f95..e5dcdbf657ddd 100644
--- a/src/joystick/windows/SDL_windowsjoystick.c
+++ b/src/joystick/windows/SDL_windowsjoystick.c
@@ -286,7 +286,7 @@ static int SDL_StartJoystickThread(void)
     }
 
     s_bJoystickThreadQuit = SDL_FALSE;
-    s_joystickThread = SDL_CreateThreadInternal(SDL_JoystickThread, "SDL_joystick", 64 * 1024, NULL);
+    s_joystickThread = SDL_CreateThreadWithStackSize(SDL_JoystickThread, "SDL_joystick", 64 * 1024, NULL);
     if (!s_joystickThread) {
         return -1;
     }
diff --git a/src/sensor/android/SDL_androidsensor.c b/src/sensor/android/SDL_androidsensor.c
index 5b5867147590e..b832974e9a170 100644
--- a/src/sensor/android/SDL_androidsensor.c
+++ b/src/sensor/android/SDL_androidsensor.c
@@ -120,7 +120,7 @@ static int SDL_ANDROID_StartSensorThread(SDL_AndroidSensorThreadContext *ctx)
     }
 
     SDL_AtomicSet(&ctx->running, SDL_TRUE);
-    ctx->thread = SDL_CreateThreadInternal(SDL_ANDROID_SensorThread, "Sensors", 0, ctx);
+    ctx->thread = SDL_CreateThread(SDL_ANDROID_SensorThread, "Sensors", ctx);
     if (!ctx->thread) {
         SDL_ANDROID_StopSensorThread(ctx);
         return -1;
diff --git a/src/thread/SDL_systhread.h b/src/thread/SDL_systhread.h
index 70df324c68ce5..c3370221dd972 100644
--- a/src/thread/SDL_systhread.h
+++ b/src/thread/SDL_systhread.h
@@ -36,13 +36,9 @@ extern "C" {
    saves a system-dependent thread id in thread->id, and returns 0
    on success.
 */
-#ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
 extern int SDL_SYS_CreateThread(SDL_Thread *thread,
-                                pfnSDL_CurrentBeginThread pfnBeginThread,
-                                pfnSDL_CurrentEndThread pfnEndThread);
-#else
-extern int SDL_SYS_CreateThread(SDL_Thread *thread);
-#endif
+                                SDL_FunctionPointer pfnBeginThread,
+                                SDL_FunctionPointer pfnEndThread);
 
 /* This function does any necessary setup in the child thread */
 extern void SDL_SYS_SetupThread(const char *name);
@@ -64,11 +60,6 @@ extern SDL_TLSData *SDL_SYS_GetTLSData(void);
 /* Set the thread local storage for this thread */
 extern int SDL_SYS_SetTLSData(SDL_TLSData *data);
 
-/* This is for internal SDL use, so we don't need #ifdefs everywhere. */
-extern SDL_Thread *
-SDL_CreateThreadInternal(int(SDLCALL *fn)(void *), const char *name,
-                         const size_t stacksize, void *data);
-
 /* Ends C function definitions when using C++ */
 #ifdef __cplusplus
 }
diff --git a/src/thread/SDL_thread.c b/src/thread/SDL_thread.c
index 1a1348e34921e..31340253eb059 100644
--- a/src/thread/SDL_thread.c
+++ b/src/thread/SDL_thread.c
@@ -307,28 +307,22 @@ void SDL_RunThread(SDL_Thread *thread)
     }
 }
 
-#ifdef SDL_CreateThread
-#undef SDL_CreateThread
-#undef SDL_CreateThreadWithStackSize
-#endif
-#if SDL_DYNAMIC_API
-#define SDL_CreateThread              SDL_CreateThread_REAL
-#define SDL_CreateThreadWithStackSize SDL_CreateThreadWithStackSize_REAL
-#endif
-
-#ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
-SDL_Thread *SDL_CreateThreadWithStackSize(int(SDLCALL *fn)(void *),
+SDL_Thread *SDL_CreateThreadWithStackSizeRuntime(SDL_ThreadFunction fn,
                               const char *name, const size_t stacksize, void *data,
-                              pfnSDL_CurrentBeginThread pfnBeginThread,
-                              pfnSDL_CurrentEndThread pfnEndThread)
-#else
-SDL_Thread *SDL_CreateThreadWithStackSize(int(SDLCALL *fn)(void *),
-                              const char *name, const size_t stacksize, void *data)
-#endif
+                              SDL_FunctionPointer pfnBeginThread,
+                              SDL_FunctionPointer pfnEndThread)
 {
     SDL_Thread *thread;
     int ret;
 
+    // rather than check this in every backend, just make sure it's correct upfront. Only allow non-NULL if non-WinRT Windows, or Microsoft GDK.
+    #if (!defined(SDL_PLATFORM_WIN32) && !defined(SDL_PLATFORM_GDK)) || defined(SDL_PLATFORM_WINRT)
+    if (pfnBeginThread || pfnEndThread) {
+        SDL_SetError("_beginthreadex/_endthreadex not supported on this platform");
+        return NULL;
+    }
+    #endif
+
     /* Allocate memory for the thread info structure */
     thread = (SDL_Thread *)SDL_calloc(1, sizeof(*thread));
     if (!thread) {
@@ -351,11 +345,7 @@ SDL_Thread *SDL_CreateThreadWithStackSize(int(SDLCALL *fn)(void *),
     thread->stacksize = stacksize;
 
     /* Create the thread and go! */
-#ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
     ret = SDL_SYS_CreateThread(thread, pfnBeginThread, pfnEndThread);
-#else
-    ret = SDL_SYS_CreateThread(thread);
-#endif
     if (ret < 0) {
         /* Oops, failed.  Gotta free everything */
         SDL_free(thread->name);
@@ -367,31 +357,12 @@ SDL_Thread *SDL_CreateThreadWithStackSize(int(SDLCALL *fn)(void *),
     return thread;
 }
 
-#ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
-SDL_Thread *SDLCALL SDL_CreateThread(int(SDLCALL *fn)(void *),
+SDL_Thread *SDL_CreateThreadRuntime(SDL_ThreadFunction fn,
                  const char *name, void *data,
-                 pfnSDL_CurrentBeginThread pfnBeginThread,
-                 pfnSDL_CurrentEndThread pfnEndThread)
-#else
-SDL_Thread *SDLCALL SDL_Create

(Patch may be truncated, please check the link at the top of this post.)