From ab205a78ce4e93689e96ab5f67c5e89ec59f6ac2 Mon Sep 17 00:00:00 2001 From: Dery Almas Date: Fri, 10 Oct 2025 23:25:00 +0200 Subject: [PATCH] Wayland: defer event thread initialization to late initialization This race condition made me pull my hair. `wl_display_roundtrip` has its own little event loop, which apparently conflicts hard with the always-running event loop thread. I kinda assumed that it would be thread-safe thanks to its internal `wl_display_prepare_read` call that the docs talk about but that's clearly not enough. Luckily this method is called very few times and the only dangerous instances are in the initialization routine, which first starts the thread and then does various roundtrips. Libdecor has also some internal roundtrips of its own which would often fail. Starting the thread after all initialization fixes the issue. Tested this by spamming *lots* of `wl_display_roundtrip` in `WaylandThread::init()` with and without this fix. --- platform/linuxbsd/wayland/wayland_thread.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/platform/linuxbsd/wayland/wayland_thread.cpp b/platform/linuxbsd/wayland/wayland_thread.cpp index a845f40c0d6..95f9fbea522 100644 --- a/platform/linuxbsd/wayland/wayland_thread.cpp +++ b/platform/linuxbsd/wayland/wayland_thread.cpp @@ -3057,6 +3057,11 @@ void WaylandThread::_poll_events_thread(void *p_data) { // Note that the main thread can still call wl_display_roundtrip as that // method directly handles all events, effectively bypassing this polling // loop and thus the mutex locking, avoiding a deadlock. + // + // WARNING: Never call `wl_display_roundtrip` inside event handlers or while + // this mutex isn't held! `wl_display_roundtrip` manually handles new events + // and if not properly gated it _will_ cause potentially stall-inducing race + // conditions. Ask me how I know. MutexLock mutex_lock(data->mutex); if (wl_display_dispatch_pending(data->wl_display) == -1) { @@ -4439,8 +4444,6 @@ Error WaylandThread::init() { thread_data.wl_display = wl_display; - events_thread.start(_poll_events_thread, &thread_data); - wl_registry = wl_display_get_registry(wl_display); ERR_FAIL_NULL_V_MSG(wl_registry, ERR_UNAVAILABLE, "Can't obtain the Wayland registry global."); @@ -4519,6 +4522,8 @@ Error WaylandThread::init() { // Update the cursor. cursor_set_shape(DisplayServer::CURSOR_ARROW); + events_thread.start(_poll_events_thread, &thread_data); + initialized = true; return OK; }