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. @@

_______________________________________________
Bf-blender-cvs mailing list
Bf-blender-cvs@blender.org
List details, subscription details or unsubscribe:
https://lists.blender.org/mailman/listinfo/bf-blender-cvs

Reply via email to