Skip to content

SDL_IsMainThread() behaves inconsistently/incorrectly on some platforms #14511

@castholm

Description

@castholm

Extracted from #12676, #14502 (comment)

#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>

int threadFunction(void * _) {
    SDL_Log("thread: CurrentThreadID: %" SDL_PRIu64, SDL_GetCurrentThreadID());
    SDL_Log("thread: IsMainThread: %d", SDL_IsMainThread());
    return 0;
}

int main(int argc, char *argv[]) {
    SDL_Thread *thread;
    if (!SDL_Init(0)) return -1;
    SDL_Log("main:   CurrentThreadID: %" SDL_PRIu64, SDL_GetCurrentThreadID());
    SDL_Log("main:   IsMainThread: %d", SDL_IsMainThread());
    thread = SDL_CreateThread(threadFunction, "thread", NULL);
    if (!thread) return -1;
    SDL_WaitThread(thread, NULL);
    return 0;
}

Compile the above program for Linux and run it.

main:   CurrentThreadID: 132321951314944
main:   IsMainThread: 1
thread: CurrentThreadID: 132321944307264
thread: IsMainThread: 1

Both threads report that they are the main thread.

Now compile the same program for Windows and run it.

main:   CurrentThreadID: 4036
main:   IsMainThread: 1
thread: CurrentThreadID: 5000
thread: IsMainThread: 0

Only one thread reports that it is the main thread.

Now update the program to call SDL_SetMainReady() before SDL_Init(), recompile for Linux and rerun it.

main:   CurrentThreadID: 126684362445824
main:   IsMainThread: 1
thread: CurrentThreadID: 126684355356224
thread: IsMainThread: 0

Only one thread reports that it is the main thread.

SDL currently flags a thread as the main thread at two points:

The root cause of the inconsistency seems to be that not all platforms call SDL_SetMainReady() (e.g. the generic SDL_RunApp() implementation used on Linux).

The docs for SDL_IsMainThread() define the main thread as follows:

On Apple platforms, the main thread is the thread that runs your program's main() entry point. On other platforms, the main thread is the one that calls SDL_Init(SDL_INIT_VIDEO), which should usually be the one that runs your program's main() entry point. If you are using the main callbacks, SDL_AppInit(), SDL_AppIterate(), and SDL_AppQuit() are all called on the main thread.

Clearly something is wrong. Either the implementation should be updated such that all threads are considered the main thread until the video subsystem is initialized on all (non-Apple) platforms, which is currently not the case for e.g. Windows, or the docs should be updated to reflect the actual behavior.

However, I would argue that a more useful definition/behavior would be something like this:

  1. If the platform has a native concept of a "main thread" (e.g. Apple platforms), that thread is always the main thread.
  2. Otherwise, if the video system has been initialized, the thread that initialized it is the main thread.
    • This lets people do SDL_Init(0) -> SDL_CreateThread() -> SDL_Init(SDL_INIT_VIDEO).
  3. Otherwise, the thread that initialized SDL by calling SDL_Init() (with any arguments) is the main thread.
    • For safety/maximum backward compatibility, SDL_SetMainReady() could still preemptively flag the calling thread as the main thread until SDL_Init() is called.
  4. Otherwise, all threads will be considered the main thread (SDL can't know about threads created outside of SDL).

Documentation should be updated to reflect this accurately. (As an aside, the docs should also clarify whether SDL_CreateThread() is allowed to be called before SDL_Init(0) (which initialized the "threading subsystem" according to the docs). Currently it's a bit unclear.)

The bug can be considered fixed when SDL_IsMainThread() in the above program returns the same value on all platforms, and related docs have been updated.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions