Clarify which thread is the main thread

If video is initialized, the main thread is the one that initialized video, otherwise if events are initialized, the main thread is the thread that initialized events, otherwise the main thread is the one that called the main function.

Fixes https://github.com/libsdl-org/SDL/issues/14511
This commit is contained in:
Sam Lantinga
2025-11-30 09:52:28 -08:00
parent 9c6c2387b9
commit 778f70c906
13 changed files with 56 additions and 46 deletions

View File

@@ -189,6 +189,8 @@ static bool SDL_MainIsReady = false;
static bool SDL_MainIsReady = true;
#endif
static SDL_ThreadID SDL_MainThreadID = 0;
static SDL_ThreadID SDL_EventsThreadID = 0;
static SDL_ThreadID SDL_VideoThreadID = 0;
static bool SDL_bInMainQuit = false;
static Uint8 SDL_SubsystemRefCount[32];
@@ -266,14 +268,16 @@ void SDL_SetMainReady(void)
bool SDL_IsMainThread(void)
{
if (SDL_MainThreadID == 0) {
// Not initialized yet?
return true;
if (SDL_VideoThreadID) {
return (SDL_GetCurrentThreadID() == SDL_VideoThreadID);
}
if (SDL_MainThreadID == SDL_GetCurrentThreadID()) {
return true;
if (SDL_EventsThreadID) {
return (SDL_GetCurrentThreadID() == SDL_EventsThreadID);
}
return false;
if (SDL_MainThreadID) {
return (SDL_GetCurrentThreadID() == SDL_MainThreadID);
}
return true;
}
// Initialize all the subsystems that require initialization before threads start
@@ -281,6 +285,11 @@ void SDL_InitMainThread(void)
{
static bool done_info = false;
// If we haven't done it by now, mark this as the main thread
if (SDL_MainThreadID == 0) {
SDL_MainThreadID = SDL_GetCurrentThreadID();
}
SDL_InitTLSData();
SDL_InitEnvironment();
SDL_InitTicks();
@@ -335,6 +344,11 @@ bool SDL_InitSubSystem(SDL_InitFlags flags)
if (flags & SDL_INIT_EVENTS) {
if (SDL_ShouldInitSubsystem(SDL_INIT_EVENTS)) {
SDL_IncrementSubsystemRefCount(SDL_INIT_EVENTS);
// Note which thread initialized events
// This is the thread which should be pumping events
SDL_EventsThreadID = SDL_GetCurrentThreadID();
if (!SDL_InitEvents()) {
SDL_DecrementSubsystemRefCount(SDL_INIT_EVENTS);
goto quit_and_error;
@@ -354,12 +368,16 @@ bool SDL_InitSubSystem(SDL_InitFlags flags)
goto quit_and_error;
}
SDL_IncrementSubsystemRefCount(SDL_INIT_VIDEO);
// We initialize video on the main thread
// On Apple platforms this is a requirement.
// On other platforms, this is the definition.
SDL_MainThreadID = SDL_GetCurrentThreadID();
SDL_VideoThreadID = SDL_GetCurrentThreadID();
#ifdef SDL_PLATFORM_APPLE
SDL_assert(SDL_VideoThreadID == SDL_MainThreadID);
#endif
SDL_IncrementSubsystemRefCount(SDL_INIT_VIDEO);
if (!SDL_VideoInit(NULL)) {
SDL_DecrementSubsystemRefCount(SDL_INIT_VIDEO);
SDL_PushError();
@@ -609,6 +627,7 @@ void SDL_QuitSubSystem(SDL_InitFlags flags)
if (SDL_ShouldQuitSubsystem(SDL_INIT_VIDEO)) {
SDL_QuitRender();
SDL_VideoQuit();
SDL_VideoThreadID = 0;
// video implies events
SDL_QuitSubSystem(SDL_INIT_EVENTS);
}
@@ -619,6 +638,7 @@ void SDL_QuitSubSystem(SDL_InitFlags flags)
if (flags & SDL_INIT_EVENTS) {
if (SDL_ShouldQuitSubsystem(SDL_INIT_EVENTS)) {
SDL_QuitEvents();
SDL_EventsThreadID = 0;
}
SDL_DecrementSubsystemRefCount(SDL_INIT_EVENTS);
}

View File

@@ -265,6 +265,7 @@ extern "C" {
#include "SDL_utils_c.h"
#include "SDL_hashtable.h"
/* SDL_ExitProcess is not declared in any public header, although
it is shared between some parts of SDL, because we don't want
anything calling it without an extremely good reason. */

View File

@@ -1494,6 +1494,9 @@ void SDL_PumpEventMaintenance(void)
// Run the system dependent event loops
static void SDL_PumpEventsInternal(bool push_sentinel)
{
// This should only be called on the main thread, check in debug builds
SDL_assert(SDL_IsMainThread());
// Free any temporary memory from old events
SDL_FreeTemporaryMemory();

View File

@@ -147,13 +147,19 @@ void SDL_QuitMainCallbacks(SDL_AppResult result)
SDL_Quit();
}
void SDL_CheckDefaultArgcArgv(int *argc, char ***argv)
static void SDL_CheckDefaultArgcArgv(int *argc, char ***argv)
{
if (!argv)
{
if (!*argv) {
static char dummyargv0[] = { 'S', 'D', 'L', '_', 'a', 'p', 'p', '\0' };
static char *argvdummy[2] = { dummyargv0, NULL };
*argc = 1;
*argv = argvdummy;
}
}
int SDL_CallMainFunction(int argc, char *argv[], SDL_main_func mainFunction)
{
SDL_CheckDefaultArgcArgv(&argc, &argv);
SDL_SetMainReady();
return mainFunction(argc, argv);
}

View File

@@ -27,10 +27,7 @@ SDL_AppResult SDL_InitMainCallbacks(int argc, char *argv[], SDL_AppInit_func app
SDL_AppResult SDL_IterateMainCallbacks(bool pump_events);
void SDL_QuitMainCallbacks(SDL_AppResult result);
// (not a callback thing, but convenient to stick this in here.)
// If *_argv is NULL, update *_argc and *_argv to point at a static array of { "SDL_app", NULL }.
void SDL_CheckDefaultArgcArgv(int *_argc, char ***_argv);
// Check args and call the main function
extern int SDL_CallMainFunction(int argc, char *argv[], SDL_main_func mainFunction);
#endif // SDL_main_callbacks_h_

View File

@@ -28,8 +28,7 @@
int SDL_RunApp(int argc, char *argv[], SDL_main_func mainFunction, void * reserved)
{
(void)reserved;
SDL_CheckDefaultArgcArgv(&argc, &argv);
return mainFunction(argc, argv);
return SDL_CallMainFunction(argc, argv, mainFunction);
}
#endif

View File

@@ -37,8 +37,6 @@ int SDL_RunApp(int argc, char *argv[], SDL_main_func mainFunction, void * reserv
{
(void)reserved;
SDL_CheckDefaultArgcArgv(&argc, &argv);
// Move any URL params that start with "SDL_" over to environment
// variables, so the hint system can pick them up, etc, much like a user
// can set them from a shell prompt on a desktop machine. Ignore all
@@ -59,7 +57,7 @@ int SDL_RunApp(int argc, char *argv[], SDL_main_func mainFunction, void * reserv
}
}, SDL_setenv_unsafe);
return mainFunction(argc, argv);
return SDL_CallMainFunction(argc, argv, mainFunction);
}
#endif

View File

@@ -24,6 +24,7 @@ extern "C" {
#include "../../core/gdk/SDL_gdk.h"
#include "../../core/windows/SDL_windows.h"
#include "../../events/SDL_events_c.h"
#include "../SDL_main_callbacks.h"
}
#include <XGameRuntime.h>
#include <xsapi-c/services_c.h>
@@ -65,14 +66,12 @@ int SDL_RunApp(int argc, char **argv, SDL_main_func mainFunction, void *reserved
SDL_SetError("[GDK] Unable to get titleid. Will not call XblInitialize. Check MicrosoftGame.config!");
}
SDL_SetMainReady();
if (!GDK_RegisterChangeNotifications()) {
return -1;
}
// Run the application main() code
result = mainFunction(argc, argv);
result = SDL_CallMainFunction(argc, argv, mainFunction);
GDK_UnregisterChangeNotifications();

View File

@@ -31,14 +31,11 @@ int SDL_RunApp(int argc, char *argv[], SDL_main_func mainFunction, void * reserv
{
int result;
SDL_CheckDefaultArgcArgv(&argc, &argv);
// init
osSetSpeedupEnable(true);
romfsInit();
SDL_SetMainReady();
result = mainFunction(argc, argv);
result = SDL_CallMainFunction(argc, argv, mainFunction);
// quit
romfsExit();

View File

@@ -68,21 +68,17 @@ static void deinit_drivers(void)
int SDL_RunApp(int argc, char *argv[], SDL_main_func mainFunction, void * reserved)
{
int res;
int result;
(void)reserved;
SDL_CheckDefaultArgcArgv(&argc, &argv);
prepare_IOP();
init_drivers();
SDL_SetMainReady();
res = mainFunction(argc, argv);
result = SDL_CallMainFunction(argc, argv, mainFunction);
deinit_drivers();
return res;
return result;
}
#endif // SDL_PLATFORM_PS2

View File

@@ -74,13 +74,9 @@ int SDL_RunApp(int argc, char *argv[], SDL_main_func mainFunction, void * reserv
{
(void)reserved;
SDL_CheckDefaultArgcArgv(&argc, &argv);
sdl_psp_setup_callbacks();
SDL_SetMainReady();
return mainFunction(argc, argv);
return SDL_CallMainFunction(argc, argv, mainFunction);
}
#endif // SDL_PLATFORM_PSP

View File

@@ -23,6 +23,7 @@
#ifdef SDL_PLATFORM_WIN32
#include "../../core/windows/SDL_windows.h"
#include "../SDL_main_callbacks.h"
/* Win32-specific SDL_RunApp(), which does most of the SDL_main work,
based on SDL_windows_main.c, placed in the public domain by Sam Lantinga 4/13/98 */
@@ -37,8 +38,7 @@ int MINGW32_FORCEALIGN SDL_RunApp(int argc, char *argv[], SDL_main_func mainFunc
if (args_error) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Fatal Error", args_error, NULL);
} else {
SDL_SetMainReady();
result = mainFunction(argc, argv);
result = SDL_CallMainFunction(argc, argv, mainFunction);
if (heap_allocated) {
HeapFree(GetProcessHeap(), 0, heap_allocated);
}

View File

@@ -42,8 +42,6 @@ static int exit_status;
int SDL_RunApp(int argc, char *argv[], SDL_main_func mainFunction, void *reserved)
{
SDL_CheckDefaultArgcArgv(&argc, &argv);
// store arguments
forward_main = mainFunction;
forward_argc = argc;
@@ -483,7 +481,7 @@ API_AVAILABLE(ios(13.0))
[self performSelector:@selector(hideLaunchScreen) withObject:nil afterDelay:0.0];
SDL_SetiOSEventPump(true);
exit_status = forward_main(forward_argc, forward_argv);
exit_status = SDL_CallMainFunction(forward_argc, forward_argv, forward_main);
SDL_SetiOSEventPump(false);
if (launchWindow) {
@@ -553,7 +551,7 @@ API_AVAILABLE(ios(13.0))
// run the user's application, passing argc and argv
SDL_SetiOSEventPump(true);
exit_status = forward_main(forward_argc, forward_argv);
exit_status = SDL_CallMainFunction(forward_argc, forward_argv, forward_main);
SDL_SetiOSEventPump(false);
if (launchWindow) {