libsdl-org / SDL

Simple Directmedia Layer
https://libsdl.org
zlib License
9.97k stars 1.84k forks source link

macos: `SDL_Init(SDL_INIT_VIDEO)` off the main thread throws an uncaught objective C exception #11437

Open maia-s opened 6 days ago

maia-s commented 6 days ago

On macos, if SDL_Init(SDL_INIT_VIDEO) is called on a thread other than the main thread, it'll throw an objective C exception and pop up a macos crash dialog.

I'd expect SDL_Init to return false and set an error message instead.

I ran into this while trying to call some SDL functions in automated tests. The test runner runs the tests in a separate thread by default.

Reproduction:

#include <SDL3/SDL.h>
#include <thread>
#include <iostream>

void thread() {
    if (SDL_Init(SDL_INIT_VIDEO)) {
        std::cout << "SDL_Init succeeded\n";
    } else {
        std::cout << "SDL_Init failed: " << SDL_GetError() << "\n";
    }
}

int main() {
    auto t = std::thread(thread);
    t.join();
}

This pops up a crash dialog and prints

*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'API misuse: setting the main menu on a non-main thread. Main menu contents should only be modified from the main thread.'

followed by a stack trace

maia-s commented 6 days ago

This happens with both SDL 2 and SDL 3

slime73 commented 5 days ago

Almost all SDL_video APIs are disallowed on non-main threads on apple platforms, not just video subsystem initialization – they'll all definitely crash if the main thread checker is enabled (https://developer.apple.com/documentation/xcode/diagnosing-memory-thread-and-crash-issues-early), and many will even without it.

Since that restriction is already documented, IMO the current behaviour is pretty reasonable compared to all the extra checks needed in SDL's code to add more graceful failures.

That link also says:

Xcode enables Main Thread Checker by default for your development-related schemes.

maia-s commented 5 days ago

I'm not asking for checks to be added to every SDL function. I don't think it's unreasonable to add a check to SDL_Init to catch this error. Every other video function requires SDL_Init to have been called first.

maia-s commented 5 days ago

Actually SDL_ShowMessageBox/SDL_ShowSimpleMessageBox should check too, as that doesn't require SDL_Init and currently just hangs forever if called on a thread

slime73 commented 5 days ago

I believe you're meant to pump events on the main thread if you call those from a non-main thread, I might be misremebering though.