[Bf-blender-cvs] [37b256e26fe] blender-v3.4-release: Fix T100855: Input while Blender is unresponsive exits under Wayland
Campbell Barton
noreply at git.blender.org
Tue Nov 15 05:31:57 CET 2022
Commit: 37b256e26feb454d9febd84dac1b1ce8b8d84d90
Author: Campbell Barton
Date: Fri Nov 11 10:57:30 2022 +1100
Branches: blender-v3.4-release
https://developer.blender.org/rB37b256e26feb454d9febd84dac1b1ce8b8d84d90
Fix T100855: Input while Blender is unresponsive exits under Wayland
Consume events in a thread to prevent Wayland's event buffer from
overflowing Waylands internal buffer and closing the connection.
>From a users perspective this seemed like a crash.
Details:
- This is a workaround for a known bug in Wayland [0].
Threaded event handling has been if-defed so it can be removed when
it's no longer needed.
- GTK & QT use threaded event handling to avoid this problem
(SDL on the other hand doesn't).
- The complexity and number of locks needed to handle events in a
separate thread is a significant down-side, but as far as I can see
this is necessary.
- Re-connecting to the Wayland server is possible but not practical as
the OpenGL context is lost and as far as I can tell it's not possible
to keep it active (see: D16492).
[0]: https://gitlab.freedesktop.org/wayland/wayland/-/issues/159
===================================================================
M intern/ghost/intern/GHOST_SystemWayland.cpp
M intern/ghost/intern/GHOST_SystemWayland.h
M intern/ghost/intern/GHOST_WindowWayland.cpp
M intern/ghost/intern/GHOST_WindowWayland.h
===================================================================
diff --git a/intern/ghost/intern/GHOST_SystemWayland.cpp b/intern/ghost/intern/GHOST_SystemWayland.cpp
index 3e45eb5904b..4f04900c457 100644
--- a/intern/ghost/intern/GHOST_SystemWayland.cpp
+++ b/intern/ghost/intern/GHOST_SystemWayland.cpp
@@ -77,6 +77,10 @@
/* Logging, use `ghost.wl.*` prefix. */
#include "CLG_log.h"
+#ifdef USE_EVENT_BACKGROUND_THREAD
+# include <pthread.h>
+#endif
+
#ifdef WITH_GHOST_WAYLAND_LIBDECOR
static bool use_libdecor = true;
# ifdef WITH_GHOST_WAYLAND_DYNLOAD
@@ -86,6 +90,12 @@ static bool has_libdecor = true;
# endif
#endif
+/* -------------------------------------------------------------------- */
+/** \name Forward Declarations
+ *
+ * Control local functionality, compositors specific workarounds.
+ * \{ */
+
static void keyboard_handle_key_repeat_cancel(struct GWL_Seat *seat);
static void output_handle_done(void *data, struct wl_output *wl_output);
@@ -105,6 +115,19 @@ static int gwl_registry_handler_interface_slot_from_string(const char *interface
static const struct GWL_RegistryHandler *gwl_registry_handler_from_interface_slot(
int interface_slot);
+#ifdef USE_EVENT_BACKGROUND_THREAD
+static void gwl_display_event_thread_destroy(GWL_Display *display);
+
+static void ghost_wl_display_lock_without_input(struct wl_display *wl_display,
+ std::mutex *server_mutex);
+#endif /* USE_EVENT_BACKGROUND_THREAD */
+
+/** In nearly all cases use `pushEvent_maybe_pending`
+ * at least when called from WAYLAND callbacks. */
+#define pushEvent DONT_USE
+
+/** \} */
+
/* -------------------------------------------------------------------- */
/** \name Workaround Compositor Specific Bugs
* \{ */
@@ -835,6 +858,26 @@ struct GWL_Display {
struct zwp_pointer_constraints_v1 *wp_pointer_constraints = nullptr;
struct zwp_pointer_gestures_v1 *wp_pointer_gestures = nullptr;
+
+ /* Threaded event handling. */
+#ifdef USE_EVENT_BACKGROUND_THREAD
+ /**
+ * Run a thread that consumes events in the background.
+ * Use `pthread` because `std::thread` leaks memory.
+ */
+ pthread_t events_pthread = 0;
+ /** Use to exit the event reading loop. */
+ bool events_pthread_is_active = false;
+
+ /**
+ * Events added from the event reading thread.
+ * Added into the main event queue when on #GHOST_SystemWayland::processEvents.
+ */
+ std::vector<GHOST_IEvent *> events_pending;
+ /** Guard against multiple threads accessing `events_pending` at once. */
+ std::mutex events_pending_mutex;
+
+#endif /* USE_EVENT_BACKGROUND_THREAD */
};
/**
@@ -845,6 +888,13 @@ struct GWL_Display {
*/
static void gwl_display_destroy(GWL_Display *display)
{
+#ifdef USE_EVENT_BACKGROUND_THREAD
+ if (display->events_pthread) {
+ ghost_wl_display_lock_without_input(display->wl_display, display->system->server_mutex);
+ display->events_pthread_is_active = false;
+ }
+#endif
+
/* For typical WAYLAND use this will always be set.
* However when WAYLAND isn't running, this will early-exit and be null. */
if (display->wl_registry) {
@@ -875,6 +925,13 @@ static void gwl_display_destroy(GWL_Display *display)
::eglTerminate(eglGetDisplay(EGLNativeDisplayType(display->wl_display)));
}
+#ifdef USE_EVENT_BACKGROUND_THREAD
+ if (display->events_pthread) {
+ gwl_display_event_thread_destroy(display);
+ display->system->server_mutex->unlock();
+ }
+#endif /* USE_EVENT_BACKGROUND_THREAD */
+
if (display->wl_display) {
wl_display_disconnect(display->wl_display);
}
@@ -1557,6 +1614,64 @@ static int ghost_wl_display_event_pump(struct wl_display *wl_display)
return err;
}
+#ifdef USE_EVENT_BACKGROUND_THREAD
+
+static void ghost_wl_display_lock_without_input(struct wl_display *wl_display,
+ std::mutex *server_mutex)
+{
+ const int fd = wl_display_get_fd(wl_display);
+ int state;
+ do {
+ state = file_descriptor_is_io_ready(fd, GWL_IOR_READ | GWL_IOR_NO_RETRY, 0);
+ /* Re-check `state` with a lock held, needed to avoid holding the lock. */
+ if (state == 0) {
+ server_mutex->lock();
+ state = file_descriptor_is_io_ready(fd, GWL_IOR_READ | GWL_IOR_NO_RETRY, 0);
+ if (state == 0) {
+ break;
+ }
+ }
+ } while (state == 0);
+}
+
+static int ghost_wl_display_event_pump_from_thread(struct wl_display *wl_display,
+ const int fd,
+ std::mutex *server_mutex)
+{
+ /* Based on SDL's `Wayland_PumpEvents`. */
+ server_mutex->lock();
+ int err = 0;
+ if (wl_display_prepare_read(wl_display) == 0) {
+ /* Use #GWL_IOR_NO_RETRY to ensure #SIGINT will break us out of our wait. */
+ if (file_descriptor_is_io_ready(fd, GWL_IOR_READ | GWL_IOR_NO_RETRY, 0) > 0) {
+ err = wl_display_read_events(wl_display);
+ }
+ else {
+ wl_display_cancel_read(wl_display);
+ }
+ }
+ else {
+ int state;
+ do {
+ server_mutex->unlock();
+ /* Wait for input (unlocked, so as not to block other threads). */
+ state = file_descriptor_is_io_ready(fd, GWL_IOR_READ | GWL_IOR_NO_RETRY, INT32_MAX);
+ server_mutex->lock();
+ /* Re-check `state` with a lock held, needed to avoid holding the lock. */
+ if (state > 0) {
+ state = file_descriptor_is_io_ready(fd, GWL_IOR_READ | GWL_IOR_NO_RETRY, 0);
+ if (state > 0) {
+ err = wl_display_dispatch_pending(wl_display);
+ }
+ }
+ } while (state > 0);
+ }
+ server_mutex->unlock();
+
+ return err;
+}
+#endif /* USE_EVENT_BACKGROUND_THREAD */
+
static size_t ghost_wl_shm_format_as_size(enum wl_shm_format format)
{
switch (format) {
@@ -1663,7 +1778,7 @@ static void keyboard_depressed_state_push_events_from_change(
for (int i = 0; i < GHOST_KEY_MODIFIER_NUM; i++) {
for (int d = seat->key_depressed.mods[i] - key_depressed_prev.mods[i]; d < 0; d++) {
const GHOST_TKey gkey = GHOST_KEY_MODIFIER_FROM_INDEX(i);
- seat->system->pushEvent(
+ seat->system->pushEvent_maybe_pending(
new GHOST_EventKey(system->getMilliSeconds(), GHOST_kEventKeyUp, win, gkey, false));
CLOG_INFO(LOG, 2, "modifier (%d) up", i);
@@ -1673,7 +1788,7 @@ static void keyboard_depressed_state_push_events_from_change(
for (int i = 0; i < GHOST_KEY_MODIFIER_NUM; i++) {
for (int d = seat->key_depressed.mods[i] - key_depressed_prev.mods[i]; d > 0; d--) {
const GHOST_TKey gkey = GHOST_KEY_MODIFIER_FROM_INDEX(i);
- seat->system->pushEvent(
+ seat->system->pushEvent_maybe_pending(
new GHOST_EventKey(system->getMilliSeconds(), GHOST_kEventKeyDown, win, gkey, false));
CLOG_INFO(LOG, 2, "modifier (%d) down", i);
}
@@ -1721,12 +1836,13 @@ static void relative_pointer_handle_relative_motion_impl(GWL_Seat *seat,
bounds.clampPoint(UNPACK2(seat->pointer.xy));
}
#endif
- seat->system->pushEvent(new GHOST_EventCursor(seat->system->getMilliSeconds(),
- GHOST_kEventCursorMove,
- win,
- wl_fixed_to_int(scale * seat->pointer.xy[0]),
- wl_fixed_to_int(scale * seat->pointer.xy[1]),
- GHOST_TABLET_DATA_NONE));
+ seat->system->pushEvent_maybe_pending(
+ new GHOST_EventCursor(seat->system->getMilliSeconds(),
+ GHOST_kEventCursorMove,
+ win,
+ wl_fixed_to_int(scale * seat->pointer.xy[0]),
+ wl_fixed_to_int(scale * seat->pointer.xy[1]),
+ GHOST_TABLET_DATA_NONE));
}
static void relative_pointer_handle_relative_motion(
@@ -1784,7 +1900,7 @@ static void dnd_events(const GWL_Seat *const seat, const GHOST_TEventType event)
const uint64_t time = seat->system->getMilliSeconds();
for (size_t i = 0; i < ARRAY_SIZE(ghost_wl_mime_preference_order_type); i++) {
const GHOST_TDragnDropTypes type = ghost_wl_mime_preference_order_type[i];
- seat->system->pushEvent(
+ seat->system->pushEvent_maybe_pending(
new GHOST_EventDragnDrop(time, event, type, win, UNPACK2(event_xy), nullptr));
}
}
@@ -2247,13 +2363,13 @@ static void data_device_handle_drop(void *data, struct wl_data_device * /*wl_dat
CLOG_INFO(LOG, 2, "drop_read_uris_fn file_count=%d", flist->count);
const wl_fixed_t scale = win->scale();
- system->pushEvent(new GHOST_EventDragnDrop(system->getMilliSeconds(),
- GHOST_kEventDraggingDropDone,
- GHOST_kDragnDropTypeFilenames,
- win,
- wl_fixed_to_int(scale * xy[0]),
- wl_fixed_to_int(scale * xy[1]),
- flist));
+ system->pushEvent_maybe_pending(new GHOST_EventDragnDrop(system->getMilliSeconds(),
+ GHOST_kEventDraggingDropDone,
+ GHOST_kDragnDropTypeFilenames,
+ win,
+ wl_fixed_to_int(scale * xy[0]),
+ wl_fixed_to_int(scale * xy[1]),
+ flist));
}
else if (ELEM(mime_receive, ghost_wl_mime_text_plain, ghost_wl_mime_text_utf8)) {
/* TODO: enable use of internal functions 'txt_insert_buf' and
@@ -2461,12 +2577,13 @@ static void pointer_handle_enter(void *data,
win->setCursorShape(win->getCursorShape());
const wl_fixed_t scale = win->scale();
- seat->system->pushEvent(new GHOST_EventCursor(seat->system->getMilliSeconds(),
- GHOST_kEventCursorMove,
-
@@ Diff output truncated at 10240 characters. @@
More information about the Bf-blender-cvs
mailing list