Browse Source

thread: Reworked SDL_CreateThread to be consistent across platforms.

Also documented missing and weird bits, rename typedefs to fit SDL standards.
Ryan C. Gordon 11 months ago
parent
commit
0ec716819e

+ 2 - 0
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()

+ 134 - 77
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
 
 /**

+ 1 - 2
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);

+ 1 - 1
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;
     }

+ 1 - 1
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);
 }

+ 1 - 1
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;
     }

+ 1 - 1
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);

+ 1 - 1
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");
         }

+ 3 - 3
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);

+ 2 - 2
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;

+ 2 - 2
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

+ 2 - 20
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)

+ 1 - 1
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)

+ 1 - 1
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;

+ 1 - 1
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;
     }

+ 1 - 1
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;

+ 2 - 11
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
 }

+ 15 - 44
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_CreateThread(int(SDLCALL *fn)(void *),
-                 const char *name, void *data)
-#endif
+                 SDL_FunctionPointer pfnBeginThread,
+                 SDL_FunctionPointer pfnEndThread)
 {
-#ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
-    return SDL_CreateThreadWithStackSize(fn, name, 0, data, pfnBeginThread, pfnEndThread);
-#else
-    return SDL_CreateThreadWithStackSize(fn, name, 0, data);
-#endif
-}
-
-SDL_Thread *SDL_CreateThreadInternal(int(SDLCALL *fn)(void *), const char *name,
-                         const size_t stacksize, void *data)
-{
-#ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
-    return SDL_CreateThreadWithStackSize(fn, name, stacksize, data, NULL, NULL);
-#else
-    return SDL_CreateThreadWithStackSize(fn, name, stacksize, data);
-#endif
+    return SDL_CreateThreadWithStackSizeRuntime(fn, name, 0, data, pfnBeginThread, pfnEndThread);
 }
 
 SDL_ThreadID SDL_GetThreadID(SDL_Thread *thread)

+ 2 - 6
src/thread/generic/SDL_systhread.c

@@ -24,13 +24,9 @@
 
 #include "../SDL_systhread.h"
 
-#ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
 int SDL_SYS_CreateThread(SDL_Thread *thread,
-                         pfnSDL_CurrentBeginThread pfnBeginThread,
-                         pfnSDL_CurrentEndThread pfnEndThread)
-#else
-int SDL_SYS_CreateThread(SDL_Thread *thread)
-#endif /* SDL_PASSED_BEGINTHREAD_ENDTHREAD */
+                         SDL_FunctionPointer pfnBeginThread,
+                         SDL_FunctionPointer pfnEndThread)
 {
     return SDL_SetError("Threads are not supported on this platform");
 }

+ 3 - 4
src/thread/n3ds/SDL_systhread.c

@@ -42,11 +42,10 @@ static void ThreadEntry(void *arg)
     threadExit(0);
 }
 
-#ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
-#error "SDL_PASSED_BEGINTHREAD_ENDTHREAD is not supported on N3DS"
-#endif
 
-int SDL_SYS_CreateThread(SDL_Thread *thread)
+int SDL_SYS_CreateThread(SDL_Thread *thread,
+                         SDL_FunctionPointer pfnBeginThread,
+                         SDL_FunctionPointer pfnEndThread)
 {
     s32 priority = 0x30;
     int cpu = -1;

+ 3 - 1
src/thread/ngage/SDL_systhread.cpp

@@ -57,7 +57,9 @@ int CreateUnique(TInt (*aFunc)(const TDesC &aName, TAny *, TAny *), TAny *aPtr1,
     return status;
 }
 
-int SDL_SYS_CreateThread(SDL_Thread *thread)
+int SDL_SYS_CreateThread(SDL_Thread *thread,
+                         SDL_FunctionPointer pfnBeginThread,
+                         SDL_FunctionPointer pfnEndThread)
 {
     RThread rthread;
 

+ 3 - 1
src/thread/ps2/SDL_systhread.c

@@ -54,7 +54,9 @@ static int childThread(void *arg)
     return res;
 }
 
-int SDL_SYS_CreateThread(SDL_Thread *thread)
+int SDL_SYS_CreateThread(SDL_Thread *thread,
+                         SDL_FunctionPointer pfnBeginThread,
+                         SDL_FunctionPointer pfnEndThread)
 {
     ee_thread_status_t status;
     ee_thread_t eethread;

+ 3 - 1
src/thread/psp/SDL_systhread.c

@@ -38,7 +38,9 @@ static int ThreadEntry(SceSize args, void *argp)
     return 0;
 }
 
-int SDL_SYS_CreateThread(SDL_Thread *thread)
+int SDL_SYS_CreateThread(SDL_Thread *thread,
+                         SDL_FunctionPointer pfnBeginThread,
+                         SDL_FunctionPointer pfnEndThread)
 {
     SceKernelThreadInfo status;
     int priority = 32;

+ 3 - 1
src/thread/pthread/SDL_systhread.c

@@ -77,7 +77,9 @@ static int (*ppthread_setname_np)(const char *) = NULL;
 static SDL_bool checked_setname = SDL_FALSE;
 static int (*ppthread_setname_np)(pthread_t, const char *) = NULL;
 #endif
-int SDL_SYS_CreateThread(SDL_Thread *thread)
+int SDL_SYS_CreateThread(SDL_Thread *thread,
+                         SDL_FunctionPointer pfnBeginThread,
+                         SDL_FunctionPointer pfnEndThread)
 {
     pthread_attr_t type;
 

+ 3 - 1
src/thread/stdcpp/SDL_systhread.cpp

@@ -41,7 +41,9 @@ static void RunThread(void *args)
 }
 
 extern "C" int
-SDL_SYS_CreateThread(SDL_Thread *thread)
+SDL_SYS_CreateThread(SDL_Thread *thread,
+                     SDL_FunctionPointer pfnBeginThread,
+                     SDL_FunctionPointer pfnEndThread)
 {
     try {
         // !!! FIXME: no way to set a thread stack size here.

+ 4 - 1
src/thread/vita/SDL_systhread.c

@@ -48,7 +48,10 @@ static int ThreadEntry(SceSize args, void *argp)
     return 0;
 }
 
-int SDL_SYS_CreateThread(SDL_Thread *thread)
+int SDL_SYS_CreateThread(SDL_Thread *thread,
+                         SDL_FunctionPointer pfnBeginThread,
+                         SDL_FunctionPointer pfnEndThread)
+
 {
     char thread_name[VITA_THREAD_NAME_MAX];
     size_t stack_size = VITA_THREAD_STACK_SIZE_DEFAULT;

+ 10 - 25
src/thread/windows/SDL_systhread.c

@@ -32,20 +32,15 @@
 #define STACK_SIZE_PARAM_IS_A_RESERVATION 0x00010000
 #endif
 
-#ifndef SDL_PASSED_BEGINTHREAD_ENDTHREAD
-/* We'll use the C library from this DLL */
-#include <process.h>
-typedef uintptr_t(__cdecl *pfnSDL_CurrentBeginThread)(void *, unsigned,
-                                                      unsigned(__stdcall *func)(void*),
-                                                      void *arg, unsigned,
-                                                      unsigned *threadID);
-typedef void(__cdecl *pfnSDL_CurrentEndThread)(unsigned code);
-#endif /* !SDL_PASSED_BEGINTHREAD_ENDTHREAD */
+typedef void (__cdecl * SDL_EndThreadExCallback) (unsigned retval);
+typedef uintptr_t (__cdecl * SDL_BeginThreadExCallback)
+                   (void *security, unsigned stacksize, unsigned (__stdcall *startaddr)(void *),
+                    void * arglist, unsigned initflag, unsigned *threadaddr);
 
 static DWORD RunThread(void *data)
 {
     SDL_Thread *thread = (SDL_Thread *)data;
-    pfnSDL_CurrentEndThread pfnEndThread = (pfnSDL_CurrentEndThread)thread->endfunc;
+    SDL_EndThreadExCallback pfnEndThread = (SDL_EndThreadExCallback)thread->endfunc;
     SDL_RunThread(thread);
     if (pfnEndThread) {
         pfnEndThread(0);
@@ -63,26 +58,16 @@ static unsigned __stdcall MINGW32_FORCEALIGN RunThreadViaBeginThreadEx(void *dat
     return (unsigned)RunThread(data);
 }
 
-#ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
 int SDL_SYS_CreateThread(SDL_Thread *thread,
-                         pfnSDL_CurrentBeginThread pfnBeginThread,
-                         pfnSDL_CurrentEndThread pfnEndThread)
+                         SDL_FunctionPointer vpfnBeginThread,
+                         SDL_FunctionPointer vpfnEndThread)
 {
-#elif defined(SDL_PLATFORM_CYGWIN) || defined(SDL_PLATFORM_WINRT)
-int SDL_SYS_CreateThread(SDL_Thread *thread)
-{
-    pfnSDL_CurrentBeginThread pfnBeginThread = NULL;
-    pfnSDL_CurrentEndThread pfnEndThread = NULL;
-#else
-int SDL_SYS_CreateThread(SDL_Thread *thread)
-{
-    pfnSDL_CurrentBeginThread pfnBeginThread = (pfnSDL_CurrentBeginThread)_beginthreadex;
-    pfnSDL_CurrentEndThread pfnEndThread = (pfnSDL_CurrentEndThread)_endthreadex;
-#endif /* SDL_PASSED_BEGINTHREAD_ENDTHREAD */
+    SDL_BeginThreadExCallback pfnBeginThread = (SDL_BeginThreadExCallback) vpfnBeginThread;
+
     const DWORD flags = thread->stacksize ? STACK_SIZE_PARAM_IS_A_RESERVATION : 0;
 
     /* Save the function which we will have to call to clear the RTL of calling app! */
-    thread->endfunc = (SDL_FunctionPointer)pfnEndThread;
+    thread->endfunc = vpfnEndThread;
 
     /* thread->stacksize == 0 means "system default", same as win32 expects */
     if (pfnBeginThread) {

+ 1 - 1
src/timer/SDL_timer.c

@@ -221,7 +221,7 @@ int SDL_InitTimers(void)
         SDL_AtomicSet(&data->active, 1);
 
         /* Timer threads use a callback into the app, so we can't set a limited stack size here. */
-        data->thread = SDL_CreateThreadInternal(SDL_TimerThread, name, 0, data);
+        data->thread = SDL_CreateThread(SDL_TimerThread, name, data);
         if (!data->thread) {
             SDL_QuitTimers();
             return -1;

+ 1 - 1
src/video/psp/SDL_pspevents.c

@@ -252,7 +252,7 @@ int PSP_EventInit(SDL_VideoDevice *_this)
         return SDL_SetError("Can't create input semaphore");
     }
     running = 1;
-    if ((thread = SDL_CreateThreadInternal(EventUpdate, "PSPInputThread", 4096, NULL)) == NULL) {
+    if ((thread = SDL_CreateThreadWithStackSize(EventUpdate, "PSPInputThread", 4096, NULL)) == NULL) {
         return SDL_SetError("Can't create input thread");
     }
     return 0;

+ 1 - 1
src/video/winrt/SDL_winrtevents.cpp

@@ -105,7 +105,7 @@ void WINRT_CycleXAMLThread(void)
 
         _mutex = SDL_CreateMutex();
         _threadState = ThreadState_Running;
-        _XAMLThread = SDL_CreateThreadInternal(WINRT_XAMLThreadMain, "SDL/XAML App Thread", 0, nullptr);
+        _XAMLThread = SDL_CreateThread(WINRT_XAMLThreadMain, "SDL/XAML App Thread", nullptr);
 
         SDL_LockMutex(_mutex);
         while (_threadState != ThreadState_Yielding) {