sdl: prevent concurrent use of SDL in different threads

sdl_gamepad.c and vo_sdl.c both have their own event loops and run in
separate threads. They don't know of each other (and shouldn't). Since
SDL only has one global event loop (why didn't they fix this in SDL2?),
these obviously clash. The actual behavior is relatively subtle, which
event being randomly dispatched to either of the threads.

This is very regrettable. Very.

Work this around. "Fortunately" SDL exposes its global state to some
degree. SDL_WasInit() returns whether a "subsystem" was initialized, and
you could say the one who initialized it owns it. Both SDL_INIT_VIDEO
and SDL_INIT_GAMECONTROLLER implicitly enable SDL_INIT_EVENTS, and the
event loop is indeed the resource that cannot be shared.

Unfortunately, this is still racy, since SDL_InitSubSystem is a second
call, and succeeds if the subsystem is already initialized (increases a
refcount I think). But good enough. Blame SDL for everything.

(I think I made this commit message too long. Nobody cares even.)

Fixes: #7085
This commit is contained in:
wm4 2019-10-25 22:17:54 +02:00
parent e63091b3cc
commit c184e290b0
2 changed files with 9 additions and 3 deletions

View File

@ -198,11 +198,17 @@ static void remove_gamepad(struct mp_input_src *src, int id)
static void read_gamepad_thread(struct mp_input_src *src, void *param)
{
if (SDL_WasInit(SDL_INIT_EVENTS)) {
MP_ERR(src, "Another component is using SDL already.\n");
mp_input_src_init_done(src);
return;
}
if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER)) {
MP_ERR(src, "SDL_Init failed\n");
mp_input_src_init_done(src);
return;
};
}
pthread_once(&events_initialized, initialize_events);

View File

@ -769,8 +769,8 @@ static int preinit(struct vo *vo)
{
struct priv *vc = vo->priv;
if (SDL_WasInit(SDL_INIT_VIDEO)) {
MP_ERR(vo, "already initialized\n");
if (SDL_WasInit(SDL_INIT_EVENTS)) {
MP_ERR(vo, "Another component is using SDL already.\n");
return -1;
}