1
0
mirror of https://github.com/godotengine/godot.git synced 2025-11-09 12:50:35 +00:00

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.
This commit is contained in:
Dery Almas
2025-10-10 23:25:00 +02:00
parent 16a11ac88b
commit ab205a78ce

View File

@@ -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;
}