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