From a1d64bbf7d91abe9f57f421774d3dab6bdd173d7 Mon Sep 17 00:00:00 2001 From: Ray Strode Date: Oct 10 2023 20:55:59 +0000 Subject: Drop real-time debugging patch --- diff --git a/0001-util-Add-way-to-print-backtraces.patch b/0001-util-Add-way-to-print-backtraces.patch deleted file mode 100644 index 0f77c15..0000000 --- a/0001-util-Add-way-to-print-backtraces.patch +++ /dev/null @@ -1,727 +0,0 @@ -From e416c06afacc05b3365103436b6cf5893be57b7b Mon Sep 17 00:00:00 2001 -From: Ray Strode -Date: Mon, 25 Sep 2023 22:22:08 +0200 -Subject: [PATCH 1/3] util: Add way to print backtraces - -Now that mutter can use a realtime thread its very important that -it doesn't stall for too long, since that can result in the kernel -killing it. Ironically, a main reason mutter could stall is -kernel bugs. - -When a stall happens, we need a way to see why. This commit adds -a new function, "meta_print_backtrace" to print backtrace of the -current process and the kernel (if possible). - -A future commit will use this new function. ---- - meson.build | 11 +++++ - meson_options.txt | 6 +++ - src/50-mutter.rules | 9 ++++ - src/core/util-private.h | 2 + - src/core/util.c | 57 ++++++++++++++++++++++++ - src/meson.build | 27 +++++++++++ - src/mutter-backtrace | 17 +++++++ - src/org.gnome.mutter.backtrace.policy.in | 17 +++++++ - 8 files changed, 146 insertions(+) - create mode 100644 src/50-mutter.rules - create mode 100755 src/mutter-backtrace - create mode 100644 src/org.gnome.mutter.backtrace.policy.in - -diff --git a/meson.build b/meson.build -index d08401b3c..15e3643c1 100644 ---- a/meson.build -+++ b/meson.build -@@ -270,60 +270,70 @@ if have_wayland or have_native_backend - libdrm_dep = dependency('libdrm') - endif - - have_egl_device = get_option('egl_device') - - have_wayland_eglstream = get_option('wayland_eglstream') - if have_wayland_eglstream - wayland_eglstream_protocols_dep = dependency('wayland-eglstream-protocols') - dl_dep = cc.find_library('dl', required: true) - - if not have_wayland - error('Wayland EGLStream support requires Wayland to be enabled') - endif - endif - - have_sm = get_option('sm') - if have_sm - sm_dep = dependency('sm') - endif - - have_libwacom = get_option('libwacom') - if have_libwacom - libwacom_dep = dependency('libwacom', version: libwacom_req) - endif - - have_pango_ft2 = get_option('pango_ft2') - if have_pango_ft2 - pangoft2_dep = dependency('pangoft2') - endif - -+have_polkit = get_option('polkit') -+if have_polkit -+ polkit_dep = dependency('polkit-gobject-1') -+ polkit_policy_dir = polkit_dep.get_variable('policydir') -+ -+ # polkit currently has no way to get rulesdir so derive it from actiondir -+ # https://gitlab.freedesktop.org/polkit/polkit/-/merge_requests/195 -+ polkit_rules_dir = join_paths(polkit_policy_dir, '..', 'rules.d') -+endif -+ - have_startup_notification = get_option('startup_notification') - if have_startup_notification - if have_x11_client - libstartup_notification_dep = dependency('libstartup-notification-1.0', - version: libstartup_notification_req) - else - error('startup_notification requires X11 or Xwayland to be enabled') - endif - endif - - have_remote_desktop = get_option('remote_desktop') - if have_remote_desktop - libpipewire_dep = dependency('libpipewire-0.3', version: libpipewire_req) - endif - - have_introspection = get_option('introspection') - if have_introspection - gobject_introspection_dep = dependency('gobject-introspection-1.0') - - introspection_args = [ - '--quiet', - '-U_GNU_SOURCE', - ] - endif - - have_documentation = get_option('docs') - if have_documentation - gidocgen_dep = dependency('gi-docgen', version: '>= 2021.1', - fallback: ['gi-docgen', 'dummy_dep']) - endif -@@ -671,50 +681,51 @@ if have_documentation - subdir('doc/reference') - endif - - gnome.post_install( - glib_compile_schemas: true, - ) - - meson.add_dist_script('meson/check-version.py', meson.project_version(), 'NEWS') - - summary('prefix', prefix, section: 'Directories') - summary('libexecdir', libexecdir, section: 'Directories') - summary('pkgdatadir', pkgdatadir, section: 'Directories') - - summary('buildtype', get_option('buildtype'), section: 'Build Configuration') - summary('debug', get_option('debug'), section: 'Build Configuration') - - summary('OpenGL', have_gl, section: 'Rendering APIs') - summary('GLES2', have_gles2, section: 'Rendering APIs') - summary('EGL', have_egl, section: 'Rendering APIs') - summary('GLX', have_glx, section: 'Rendering APIs') - - summary('Wayland', have_wayland, section: 'Options') - summary('Wayland EGLStream', have_wayland_eglstream, section: 'Options') - summary('X11', have_x11, section: 'Options') - summary('XWayland', have_xwayland, section: 'Options') - summary('Native Backend', have_native_backend, section: 'Options') - summary('EGL Device', have_egl_device, section: 'Options') - summary('Remote desktop', have_remote_desktop, section: 'Options') - summary('libgnome-desktop', have_gnome_desktop, section: 'Options') - summary('libdisplay-info', have_libdisplay_info, section: 'Options') -+summary('Polkit enhanced backtrace support', have_polkit, section: 'Options') - summary('Sound player', have_sound_player, section: 'Options') - summary('gudev', have_libgudev, section: 'Options') - summary('Wacom', have_libwacom, section: 'Options') - summary('SM', have_sm, section: 'Options') - summary('Startup notification', have_startup_notification, section: 'Options') - summary('Introspection', have_introspection, section: 'Options') - summary('Documentation', have_documentation, section: 'Options') - summary('Profiler', have_profiler, section: 'Options') - summary('Xwayland initfd', have_xwayland_initfd, section: 'Options') - summary('Xwayland listenfd', have_xwayland_listenfd, section: 'Options') - summary('Xwayland terminate delay', have_xwayland_terminate_delay, section: 'Options') - summary('Xwayland byte-swapped clients', have_xwayland_byte_swapped_clients, section: 'Options') - - summary('Enabled', have_tests, section: 'Tests') - summary('Core tests', have_core_tests, section: 'Tests') - summary('Cogl tests', have_cogl_tests, section: 'Tests') - summary('Clutter tests', have_clutter_tests, section: 'Tests') - summary('KVM tests', get_option('kvm_tests'), section: 'Tests') - summary('Installed tests', have_installed_tests, section: 'Tests') - summary('Coverage', get_option('b_coverage'), section: 'Tests') -diff --git a/meson_options.txt b/meson_options.txt -index b5d215b24..b6b18ebdf 100644 ---- a/meson_options.txt -+++ b/meson_options.txt -@@ -84,60 +84,66 @@ option('wayland_eglstream', - option('udev', - type: 'boolean', - value: true, - description: 'Enable udev support when using the X11 backend' - ) - - option('udev_dir', - type: 'string', - value: '', - description: 'Absolute path of the udev base directory' - ) - - option('libwacom', - type: 'boolean', - value: true, - description: 'Enable libwacom support' - ) - - option('sound_player', - type: 'boolean', - value: true, - description: 'Enable sound player support using libcanberra', - ) - - option('pango_ft2', - type: 'boolean', - value: true, - description: 'Enable PangoFt2 support' - ) - -+option('polkit', -+ type: 'boolean', -+ value: true, -+ description: 'Enable Polkit enhanced backtrace support' -+) -+ - option('startup_notification', - type: 'boolean', - value: true, - description: 'Enable startup notification support' - ) - - option('sm', - type: 'boolean', - value: true, - description: 'Enable X11 session management support' - ) - - option('introspection', - type: 'boolean', - value: true, - description: 'Enable GObject introspection' - ) - - option('docs', - type: 'boolean', - value: false, - description: 'Enable gi-docgen documentation' - ) - - option('cogl_tests', - type: 'boolean', - value: true, - description: 'Enable cogl tests' - ) - -diff --git a/src/50-mutter.rules b/src/50-mutter.rules -new file mode 100644 -index 000000000..d14735fe6 ---- /dev/null -+++ b/src/50-mutter.rules -@@ -0,0 +1,9 @@ -+polkit.addRule(function(action, subject) { -+ if (subject.isInGroup("wheel") && -+ subject.active && -+ subject.local && -+ action.id == "org.gnome.mutter.backtrace") { -+ return polkit.Result.YES; -+ } -+}); -+ -diff --git a/src/core/util-private.h b/src/core/util-private.h -index 18ae68de7..32798ca4e 100644 ---- a/src/core/util-private.h -+++ b/src/core/util-private.h -@@ -13,46 +13,48 @@ - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - */ - - #pragma once - - #include - #include - - #include "meta/util.h" - #include "meta/common.h" - - /* META_EXPORT_TEST should be used to export symbols that are exported only - * for testability purposes */ - #define META_EXPORT_TEST META_EXPORT - - void meta_set_verbose (gboolean setting); - void meta_set_debugging (gboolean setting); - - void meta_set_is_wayland_compositor (gboolean setting); - - char * meta_generate_random_id (GRand *rand, - int length); - -+void meta_print_backtrace (void); -+ - void meta_init_debug_utils (void); - - static inline int64_t - meta_timeval_to_microseconds (const struct timeval *tv) - { - return ((int64_t) tv->tv_sec) * G_USEC_PER_SEC + tv->tv_usec; - } - - #define META_POINT_IN_RECT(xcoord, ycoord, rect) \ - ((xcoord) >= (rect).x && \ - (xcoord) < ((rect).x + (rect).width) && \ - (ycoord) >= (rect).y && \ - (ycoord) < ((rect).y + (rect).height)) - - #define META_CONTAINER_OF(ptr, type, member) \ - (type *) ((uint8_t *) (ptr) - G_STRUCT_OFFSET (type, member)) -diff --git a/src/core/util.c b/src/core/util.c -index 05a0dea39..dc547713c 100644 ---- a/src/core/util.c -+++ b/src/core/util.c -@@ -1,107 +1,110 @@ - /* - * Copyright (C) 2001 Havoc Pennington - * Copyright (C) 2005 Elijah Newren - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - */ - - - #define _POSIX_C_SOURCE 200112L /* for fdopen() */ - - #include "config.h" - - #include "core/display-private.h" - #include "core/util-private.h" - -+#include - #include - #include - #include - #include - #include -+#include - - #ifdef HAVE_SYS_PRCTL - #include - #endif - - #include "clutter/clutter-mutter.h" - #include "cogl/cogl.h" - #include "meta/common.h" - #include "meta/main.h" - - static const GDebugKey meta_debug_keys[] = { - { "focus", META_DEBUG_FOCUS }, - { "workarea", META_DEBUG_WORKAREA }, - { "stack", META_DEBUG_STACK }, - { "sm", META_DEBUG_SM }, - { "events", META_DEBUG_EVENTS }, - { "window-state", META_DEBUG_WINDOW_STATE }, - { "window-ops", META_DEBUG_WINDOW_OPS }, - { "geometry", META_DEBUG_GEOMETRY }, - { "placement", META_DEBUG_PLACEMENT }, - { "ping", META_DEBUG_PING }, - { "keybindings", META_DEBUG_KEYBINDINGS }, - { "sync", META_DEBUG_SYNC }, - { "startup", META_DEBUG_STARTUP }, - { "prefs", META_DEBUG_PREFS }, - { "groups", META_DEBUG_GROUPS }, - { "resizing", META_DEBUG_RESIZING }, - { "shapes", META_DEBUG_SHAPES }, - { "edge-resistance", META_DEBUG_EDGE_RESISTANCE }, - { "dbus", META_DEBUG_DBUS }, - { "input", META_DEBUG_INPUT }, - { "wayland", META_DEBUG_WAYLAND }, - { "kms", META_DEBUG_KMS }, - { "screen-cast", META_DEBUG_SCREEN_CAST }, - { "remote-desktop", META_DEBUG_REMOTE_DESKTOP }, - { "backend", META_DEBUG_BACKEND }, - { "render", META_DEBUG_RENDER }, - { "color", META_DEBUG_COLOR }, - { "input-events", META_DEBUG_INPUT_EVENTS }, - { "eis", META_DEBUG_EIS }, - }; - - static gint verbose_topics = 0; - static gboolean is_wayland_compositor = FALSE; - static int debug_paint_flags = 0; - static GLogLevelFlags mutter_log_level = G_LOG_LEVEL_MESSAGE; -+static char *backtrace_command = NULL; - - #ifdef WITH_VERBOSE_MODE - static FILE* logfile = NULL; - - static void - ensure_logfile (void) - { - if (logfile == NULL && g_getenv ("MUTTER_USE_LOGFILE")) - { - char *filename = NULL; - char *tmpl; - int fd; - GError *err; - - tmpl = g_strdup_printf ("mutter-%d-debug-log-XXXXXX", - (int) getpid ()); - - err = NULL; - fd = g_file_open_tmp (tmpl, - &filename, - &err); - - g_free (tmpl); - - if (err != NULL) - { - meta_warning ("Failed to open debug log: %s", - err->message); - g_error_free (err); - return; -@@ -186,60 +189,70 @@ meta_remove_verbose_topic (MetaDebugTopic topic) - verbose_topics = 0; - else - verbose_topics &= ~topic; - } - - void - meta_init_debug_utils (void) - { - const char *debug_env; - - #ifdef HAVE_SYS_PRCTL - prctl (PR_SET_DUMPABLE, 1); - #endif - - if (g_getenv ("MUTTER_VERBOSE")) - meta_set_verbose (TRUE); - - debug_env = g_getenv ("MUTTER_DEBUG"); - if (debug_env) - { - MetaDebugTopic topics; - - topics = g_parse_debug_string (debug_env, - meta_debug_keys, - G_N_ELEMENTS (meta_debug_keys)); - meta_add_verbose_topic (topics); - } - - if (g_test_initialized ()) - mutter_log_level = G_LOG_LEVEL_DEBUG; -+ -+ /* If pkexec works we'll get a kernel backtrace too, -+ * but it may not work if the user isn't in the wheel group. -+ * If it fails, we fall back to running unprivileged to at -+ * least get a backtrace of the process. -+ */ -+ if (backtrace_command == NULL) -+ backtrace_command = g_strdup_printf ("pkexec %1$s %2$d || %1$s %2$d", -+ MUTTER_LIBEXECDIR "/mutter-backtrace", -+ (int) getpid ()); - } - - gboolean - meta_is_wayland_compositor (void) - { - return is_wayland_compositor; - } - - void - meta_set_is_wayland_compositor (gboolean value) - { - is_wayland_compositor = value; - } - - char * - meta_g_utf8_strndup (const gchar *src, - gsize n) - { - const gchar *s = src; - while (n && *s) - { - s = g_utf8_next_char (s); - n--; - } - - return g_strndup (src, s - src); - } - - static int - utf8_fputs (const char *str, -@@ -513,60 +526,104 @@ MetaLocaleDirection - meta_get_locale_direction (void) - { - switch (clutter_get_text_direction ()) - { - case CLUTTER_TEXT_DIRECTION_LTR: - return META_LOCALE_DIRECTION_LTR; - case CLUTTER_TEXT_DIRECTION_RTL: - return META_LOCALE_DIRECTION_RTL; - default: - g_assert_not_reached (); - return 0; - } - } - - char * - meta_generate_random_id (GRand *rand, - int length) - { - char *id; - int i; - - /* Generate a random string of printable ASCII characters. */ - - id = g_new0 (char, length + 1); - for (i = 0; i < length; i++) - id[i] = (char) g_rand_int_range (rand, 32, 127); - - return id; - } - -+static gboolean -+unix_signal_safe_run_command (const char *command) -+{ -+ pid_t pid; -+ int status; -+ int ret; -+ -+ pid = fork (); -+ if (pid == -1) -+ return FALSE; -+ -+ if (pid == 0) -+ { -+ int stdin_fd; -+ -+ const char *argv[] = { -+ "/bin/sh", -+ "-c", -+ command, -+ NULL -+ }; -+ -+ stdin_fd = open ("/dev/null", O_RDWR); -+ dup2 (stdin_fd, STDIN_FILENO); -+ -+ execve (argv[0], (char **) argv, environ); -+ -+ _exit (1); -+ } -+ -+ do -+ { -+ ret = waitpid (pid, &status, 0); -+ } -+ while (ret != 0 && errno == EINTR); -+ -+ return WIFEXITED (status) && WEXITSTATUS (status) == 0; -+} -+ -+void -+meta_print_backtrace (void) -+{ -+ unix_signal_safe_run_command (backtrace_command); -+} - - void - meta_add_clutter_debug_flags (ClutterDebugFlag debug_flags, - ClutterDrawDebugFlag draw_flags, - ClutterPickDebugFlag pick_flags) - { - clutter_add_debug_flags (debug_flags, draw_flags, pick_flags); - } - - void - meta_remove_clutter_debug_flags (ClutterDebugFlag debug_flags, - ClutterDrawDebugFlag draw_flags, - ClutterPickDebugFlag pick_flags) - { - clutter_remove_debug_flags (debug_flags, draw_flags, pick_flags); - } - - /** - * meta_get_clutter_debug_flags: - * @debug_flags: (out) (optional): return location for debug flags - * @draw_flags: (out) (optional): return location for draw debug flags - * @pick_flags: (out) (optional): return location for pick debug flags - */ - void - meta_get_clutter_debug_flags (ClutterDebugFlag *debug_flags, - ClutterDrawDebugFlag *draw_flags, - ClutterPickDebugFlag *pick_flags) - { - clutter_get_debug_flags (debug_flags, draw_flags, pick_flags); - } -diff --git a/src/meson.build b/src/meson.build -index ca2ef166c..ea3614936 100644 ---- a/src/meson.build -+++ b/src/meson.build -@@ -1197,60 +1197,87 @@ libmutter = shared_library(libmutter_name, - ], - install_rpath: pkglibdir, - install_dir: libdir, - install: true, - ) - - libmutter_dep = declare_dependency( - link_with: libmutter, - include_directories: mutter_includes, - sources: mutter_built_headers, - dependencies: [ - libmutter_cogl_dep, - libmutter_clutter_dep, - mutter_deps, - ], - ) - - mutter = executable('mutter', - sources: [ - files('core/mutter.c'), - ], - include_directories: mutter_includes, - c_args: [ - mutter_c_args, - '-DG_LOG_DOMAIN="mutter"', - ], - dependencies: [libmutter_dep], - install_dir: bindir, - install: true, - ) -+ -+install_data( -+ 'mutter-backtrace', -+ install_dir : libexecdir, -+ install_mode : 'rwxr-xr-x' -+) -+ -+if have_polkit -+ backtrace_polkit_policy = configure_file( -+ input : 'org.gnome.mutter.backtrace.policy.in', -+ output : 'org.gnome.mutter.backtrace.policy', -+ configuration : {'LIBEXECDIR': libexecdir} -+ ) -+ -+ install_data( -+ backtrace_polkit_policy, -+ install_dir : polkit_policy_dir, -+ install_mode : 'rw-r--r--' -+ ) -+ -+ install_data( -+ '50-mutter.rules', -+ install_dir : polkit_rules_dir, -+ install_mode : 'rw-r--r--' -+ ) -+endif -+ - if have_x11 - executable('mutter-restart-helper', - sources: [ - files('core/restart-helper.c'), - ], - include_directories: [ - top_includepath, - ], - c_args: [ - mutter_c_args, - '-DG_LOG_DOMAIN="mutter-restart-helper"', - ], - dependencies: [ - x11_dep, - xcomposite_dep, - ], - install_dir: libexecdir, - install: true, - ) - endif - - if have_introspection - mutter_introspected_sources = [] - foreach source : mutter_sources - if source.endswith('.c') - mutter_introspected_sources += source - endif - endforeach - - libmutter_gir = gnome.generate_gir(libmutter, -diff --git a/src/mutter-backtrace b/src/mutter-backtrace -new file mode 100755 -index 000000000..20c9d4e75 ---- /dev/null -+++ b/src/mutter-backtrace -@@ -0,0 +1,17 @@ -+#!/bin/bash -+ -+[ $# -ne 1 ] && exit 1 -+ -+gdb_command=(gdb -p "$1" -ex 'thread apply all bt full' -batch) -+ -+if [ -n $PKEXEC_UID -a $(id -u) -eq 0 ]; then -+ cd /proc/$1/task -+ for thread in *; do -+ echo "Thread ${thread}:" -+ cat $thread/stack -+ done -+ pkexec --user $(id -u -n $PKEXEC_UID) "${gdb_command[@]}" -+ exit 0 -+fi -+ -+${gdb_command[@]} -diff --git a/src/org.gnome.mutter.backtrace.policy.in b/src/org.gnome.mutter.backtrace.policy.in -new file mode 100644 -index 000000000..39985acb9 ---- /dev/null -+++ b/src/org.gnome.mutter.backtrace.policy.in -@@ -0,0 +1,17 @@ -+ -+ -+ -+ -+ Authentication is required to run mutter-backtrace -+ dialog-password -+ -+ auth_admin -+ auth_admin -+ auth_admin -+ -+ @LIBEXECDIR@/mutter-backtrace -+ -+ -+ --- -2.41.0 - diff --git a/0002-clutter-Add-ms2ns-helper.patch b/0002-clutter-Add-ms2ns-helper.patch deleted file mode 100644 index e9c21d0..0000000 --- a/0002-clutter-Add-ms2ns-helper.patch +++ /dev/null @@ -1,89 +0,0 @@ -From 555d200c63051228062367316859e255abf6ab00 Mon Sep 17 00:00:00 2001 -From: Ray Strode -Date: Fri, 29 Sep 2023 10:53:50 -0400 -Subject: [PATCH 2/3] clutter: Add ms2ns helper - -Some system apis we're going to need in the future use -nanoseconds, but we're never going to need more timing -precision that milliseconds. - -In preparate for that, this commit adds a new helper ns2ms -to help avoid bugs in unit conversion. ---- - clutter/clutter/clutter-private.h | 6 ++++++ - 1 file changed, 6 insertions(+) - -diff --git a/clutter/clutter/clutter-private.h b/clutter/clutter/clutter-private.h -index 9d5e68c8c..007e15eba 100644 ---- a/clutter/clutter/clutter-private.h -+++ b/clutter/clutter/clutter-private.h -@@ -219,60 +219,66 @@ gboolean _clutter_run_progress_function (GType gtype, - const GValue *final, - gdouble progress, - GValue *retval); - - void clutter_timeline_cancel_delay (ClutterTimeline *timeline); - - static inline void - clutter_round_to_256ths (float *f) - { - *f = roundf ((*f) * 256) / 256; - } - - static inline uint64_t - ns (uint64_t ns) - { - return ns; - } - - static inline int64_t - us (int64_t us) - { - return us; - } - - static inline int64_t - ms (int64_t ms) - { - return ms; - } - -+static inline int64_t -+ms2ns (int64_t ms) -+{ -+ return ns (us (ms * 1000) * 1000); -+} -+ - static inline int64_t - ms2us (int64_t ms) - { - return us (ms * 1000); - } - - static inline int64_t - us2ns (int64_t us) - { - return ns (us * 1000); - } - - static inline int64_t - us2ms (int64_t us) - { - return (int64_t) (us / 1000); - } - - static inline int64_t - ns2us (int64_t ns) - { - return us (ns / 1000); - } - - static inline int64_t - s2us (int64_t s) - { - return s * G_USEC_PER_SEC; - } - --- -2.41.0 - diff --git a/0003-native-Stop-using-real-time-thread-if-it-stalls.patch b/0003-native-Stop-using-real-time-thread-if-it-stalls.patch deleted file mode 100644 index 335ea97..0000000 --- a/0003-native-Stop-using-real-time-thread-if-it-stalls.patch +++ /dev/null @@ -1,1074 +0,0 @@ -From 637d2cfab9421fbcd87fef72f6ce7347b7722c13 Mon Sep 17 00:00:00 2001 -From: Ray Strode -Date: Mon, 25 Sep 2023 22:29:17 +0200 -Subject: [PATCH 3/3] native: Stop using real-time thread if it stalls - -RTKit requires mutter to set a hard rlimit on how long its thread will -use CPU before going back to the kernel. This limit is about 12 frames. - -Unfortunately, amdgpu seems to have a bug at the moment where it will -cause the thread to stall longer than that when the screen is locked. - -It also seemingly stalls during some games. - -Rather than let the display server get slayed, this commit adds a -roundtrip through the kernel to reset the clock when things are stalled, -and tells mutter to switch away from using real-time threads. - -Closes https://gitlab.gnome.org/GNOME/mutter/-/issues/3037 ---- - src/backends/native/meta-thread-watcher.c | 419 ++++++++++++++++++++++ - src/backends/native/meta-thread-watcher.h | 36 ++ - src/backends/native/meta-thread.c | 65 +++- - src/meson.build | 3 +- - 4 files changed, 520 insertions(+), 3 deletions(-) - create mode 100644 src/backends/native/meta-thread-watcher.c - create mode 100644 src/backends/native/meta-thread-watcher.h - -diff --git a/src/backends/native/meta-thread-watcher.c b/src/backends/native/meta-thread-watcher.c -new file mode 100644 -index 000000000..439966ef2 ---- /dev/null -+++ b/src/backends/native/meta-thread-watcher.c -@@ -0,0 +1,419 @@ -+/* -+ * Copyright (C) 2023 Red Hat -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License as -+ * published by the Free Software Foundation; either version 2 of the -+ * License, or (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, but -+ * WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program; if not, see . -+ */ -+ -+#include "config.h" -+ -+#include "backends/native/meta-thread-watcher.h" -+ -+#include "clutter/clutter.h" -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "clutter/clutter-private.h" -+#include "core/util-private.h" -+ -+/* There's a watchdog timer that if left to its own devices will fire after -+ * priv->interval_ms milliseconds. -+ * -+ * There's a GLib main loop timeout that runs just before the watchdog timer fires -+ * to reset the timer to run later. -+ * -+ * If the main loop is ever blocked the main loop timeout won't run and the timer -+ * wont get reset. In this way we can tell if the thread with the main loop is stalled. -+ * -+ * WATCH_INTERVAL_PHASE_OFFSET_MS is how many milliseconds before the watchdog timer -+ * is supposed to fire that the main loop timeout is supposed to reset the timer. -+ * -+ * It just needs to be long enough for timer_settime to be called, but there's -+ * no real disadvantage to making it longer, so long as it's less the thread rlimit. -+ * -+ * It's currently set somewhat arbitrarily at 16ms (approximately one rendererd frame -+ * on most machines) -+ */ -+#define WATCH_INTERVAL_PHASE_OFFSET_MS ms (16) -+ -+enum -+{ -+ THREAD_STALLED, -+ -+ N_SIGNALS -+}; -+ -+static guint signals[N_SIGNALS]; -+ -+struct _MetaThreadWatcher -+{ -+ GObject parent_instance; -+}; -+ -+typedef struct _MetaThreadWatcherPrivate -+{ -+ int fds[2]; -+ int64_t interval_ms; -+ timer_t *timer; -+ guint notification_watch_id; -+ GMainContext *context; -+ GSource *source; -+} MetaThreadWatcherPrivate; -+ -+G_DEFINE_TYPE_WITH_PRIVATE (MetaThreadWatcher, meta_thread_watcher, G_TYPE_OBJECT) -+ -+static void -+meta_thread_watcher_constructed (GObject *object) -+{ -+ MetaThreadWatcher *watcher = META_THREAD_WATCHER (object); -+ MetaThreadWatcherPrivate *priv = meta_thread_watcher_get_instance_private (watcher); -+ -+ priv->timer = NULL; -+ priv->fds[0] = -1; -+ priv->fds[1] = -1; -+ -+ G_OBJECT_CLASS (meta_thread_watcher_parent_class)->constructed (object); -+} -+ -+static void -+close_fds (MetaThreadWatcher *watcher) -+{ -+ MetaThreadWatcherPrivate *priv = meta_thread_watcher_get_instance_private (watcher); -+ -+ g_clear_fd (&priv->fds[0], NULL); -+ g_clear_fd (&priv->fds[1], NULL); -+} -+ -+static void -+notify_watched_thread (int fd) -+{ -+ ssize_t bytes_written = 0; -+ -+ do -+ { -+ bytes_written = write (fd, "", 1); -+ } -+ while (bytes_written < 0 && errno == EINTR); -+} -+ -+static void -+clear_notifications (MetaThreadWatcher *watcher) -+{ -+ MetaThreadWatcherPrivate *priv = meta_thread_watcher_get_instance_private (watcher); -+ ssize_t bytes_read; -+ char buf[64]; -+ -+ do -+ { -+ bytes_read = read (priv->fds[0], buf, sizeof (buf)); -+ } -+ while (bytes_read > 0 || (bytes_read < 0 && errno == EINTR)); -+} -+ -+static void -+yield (void) -+{ -+ struct pollfd sleep_pollfd = { -1, 0, 0 }; -+ poll (&sleep_pollfd, 0, 1); -+} -+ -+static void -+on_xcpu_signal (int signal, -+ siginfo_t *signal_data, -+ void *context) -+{ -+ int fd; -+ static gboolean backtrace_printed = FALSE; -+ -+ /* If we're getting the XCPU signal that means the realtime thread is blocked and -+ * mutter is at risk of being killed by the kernel. We can placate the kernel by -+ * sleeping for a millisecond or so. That should buy us another ~200ms to tear -+ * down the realtime thread and get out of the dangerzone. -+ */ -+ yield (); -+ -+ /* If we're here, there's a bug somewhere, so send backtraces to the journal. -+ */ -+ if (!backtrace_printed) -+ { -+ const char *message = "Hang in realtime thread detected! Backtrace:\n"; -+ write (STDERR_FILENO, message, strlen (message)); -+ meta_print_backtrace (); -+ backtrace_printed = TRUE; -+ } -+ -+ if (signal_data->si_pid != 0 || signal_data->si_code != SI_TIMER) -+ return; -+ -+ fd = signal_data->si_value.sival_int; -+ notify_watched_thread (fd); -+} -+ -+static void -+meta_thread_watcher_finalize (GObject *object) -+{ -+ MetaThreadWatcher *watcher = META_THREAD_WATCHER (object); -+ -+ meta_thread_watcher_stop (watcher); -+ G_OBJECT_CLASS (meta_thread_watcher_parent_class)->finalize (object); -+} -+ -+static void -+meta_thread_watcher_class_init (MetaThreadWatcherClass *klass) -+{ -+ GObjectClass *object_class = G_OBJECT_CLASS (klass); -+ struct sigaction signal_request; -+ int ret; -+ -+ object_class->constructed = meta_thread_watcher_constructed; -+ object_class->finalize = meta_thread_watcher_finalize; -+ -+ signals[THREAD_STALLED] = -+ g_signal_new ("thread-stalled", -+ G_TYPE_FROM_CLASS (klass), -+ G_SIGNAL_RUN_LAST, -+ 0, -+ NULL, NULL, NULL, -+ G_TYPE_NONE, 0); -+ -+ signal_request.sa_flags = SA_SIGINFO; -+ signal_request.sa_sigaction = on_xcpu_signal; -+ sigemptyset (&signal_request.sa_mask); -+ ret = sigaction (SIGXCPU, &signal_request, NULL); -+ -+ if (ret == -1) -+ g_warning ("Failed to listen for SIGXCPU signal: %m"); -+} -+ -+static void -+meta_thread_watcher_init (MetaThreadWatcher *thread_watcher) -+{ -+} -+ -+MetaThreadWatcher * -+meta_thread_watcher_new (void) -+{ -+ MetaThreadWatcher *watcher; -+ -+ watcher = g_object_new (META_TYPE_THREAD_WATCHER, NULL); -+ -+ return watcher; -+} -+ -+static gboolean -+on_reset_timer (MetaThreadWatcher *watcher) -+{ -+ g_autoptr (GError) error = NULL; -+ gboolean was_reset; -+ -+ if (!meta_thread_watcher_is_started (watcher)) -+ return G_SOURCE_REMOVE; -+ -+ was_reset = meta_thread_watcher_reset (watcher, &error); -+ -+ if (!was_reset) -+ { -+ g_warning ("Failed to reset real-time thread watchdog timer: %s", -+ error->message); -+ return G_SOURCE_REMOVE; -+ } -+ -+ return G_SOURCE_CONTINUE; -+} -+ -+void -+meta_thread_watcher_attach (MetaThreadWatcher *watcher, -+ GMainContext *context) -+{ -+ MetaThreadWatcherPrivate *priv = meta_thread_watcher_get_instance_private (watcher); -+ g_return_if_fail (META_IS_THREAD_WATCHER (watcher)); -+ g_return_if_fail (priv->source == NULL); -+ -+ g_clear_pointer (&priv->context, g_main_context_unref); -+ priv->context = g_main_context_ref (context); -+} -+ -+void -+meta_thread_watcher_detach (MetaThreadWatcher *watcher) -+{ -+ MetaThreadWatcherPrivate *priv = meta_thread_watcher_get_instance_private (watcher); -+ -+ g_return_if_fail (META_IS_THREAD_WATCHER (watcher)); -+ -+ g_clear_pointer (&priv->source, g_source_destroy); -+ g_clear_pointer (&priv->context, g_main_context_unref); -+} -+ -+static gboolean -+on_thread_stalled (int fd, -+ GIOCondition condition, -+ MetaThreadWatcher *watcher) -+{ -+ if (condition & G_IO_IN) -+ clear_notifications (watcher); -+ -+ if (meta_thread_watcher_is_started (watcher)) -+ g_signal_emit (G_OBJECT (watcher), signals[THREAD_STALLED], 0); -+ -+ return G_SOURCE_REMOVE; -+} -+ -+gboolean -+meta_thread_watcher_is_started (MetaThreadWatcher *watcher) -+{ -+ MetaThreadWatcherPrivate *priv = meta_thread_watcher_get_instance_private (watcher); -+ -+ g_return_val_if_fail (META_IS_THREAD_WATCHER (watcher), FALSE); -+ -+ return priv->timer != NULL; -+} -+ -+static void -+meta_thread_watcher_clear_source (MetaThreadWatcher *watcher) -+{ -+ MetaThreadWatcherPrivate *priv = meta_thread_watcher_get_instance_private (watcher); -+ priv->source = NULL; -+} -+ -+gboolean -+meta_thread_watcher_start (MetaThreadWatcher *watcher, -+ int interval_us, -+ GError **error) -+{ -+ MetaThreadWatcherPrivate *priv = meta_thread_watcher_get_instance_private (watcher); -+ GSource *source = NULL; -+ sigevent_t timer_request; -+ int ret; -+ g_autofree timer_t *timer = NULL; -+ -+ g_return_val_if_fail (META_IS_THREAD_WATCHER (watcher), FALSE); -+ g_return_val_if_fail (interval_us > ms2us (WATCH_INTERVAL_PHASE_OFFSET_MS), FALSE); -+ g_return_val_if_fail (priv->context != NULL, FALSE); -+ -+ if (meta_thread_watcher_is_started (watcher)) -+ return TRUE; -+ -+ priv->interval_ms = us2ms (interval_us); -+ -+ if (!g_unix_open_pipe (priv->fds, -+ FD_CLOEXEC | O_NONBLOCK, -+ error)) -+ return FALSE; -+ -+ if (priv->fds[0] == -1 || priv->fds[1] == -1) -+ { -+ g_set_error (error, G_IO_ERROR, g_io_error_from_errno (EBADF), -+ "Thread watcher could not create pipe"); -+ return FALSE; -+ } -+ -+ timer_request.sigev_notify = SIGEV_THREAD_ID; -+ timer_request.sigev_signo = SIGXCPU; -+ timer_request.sigev_value.sival_int = priv->fds[1]; -+ timer_request._sigev_un._tid = gettid (); -+ timer = g_new0 (timer_t, 1); -+ ret = timer_create (CLOCK_THREAD_CPUTIME_ID, &timer_request, timer); -+ -+ if (ret == -1) -+ { -+ g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), -+ "Failed to create unix timer: %s", g_strerror (errno)); -+ return FALSE; -+ } -+ -+ priv->timer = g_steal_pointer (&timer); -+ -+ if (!meta_thread_watcher_reset (watcher, error)) -+ return FALSE; -+ -+ priv->notification_watch_id = g_unix_fd_add (priv->fds[0], -+ G_IO_IN, -+ (GUnixFDSourceFunc) on_thread_stalled, -+ watcher); -+ -+ source = g_timeout_source_new (priv->interval_ms - WATCH_INTERVAL_PHASE_OFFSET_MS); -+ g_source_set_name (source, "[mutter] Thread watcher"); -+ g_source_set_callback (source, -+ (GSourceFunc) on_reset_timer, -+ watcher, -+ (GDestroyNotify) -+ meta_thread_watcher_clear_source); -+ g_source_attach (source, priv->context); -+ g_source_unref (source); -+ -+ priv->source = source; -+ -+ return TRUE; -+} -+ -+static void -+free_timer (timer_t *timer) -+{ -+ if (!timer) -+ return; -+ -+ timer_delete (*timer); -+} -+ -+gboolean -+meta_thread_watcher_reset (MetaThreadWatcher *watcher, -+ GError **error) -+{ -+ MetaThreadWatcherPrivate *priv = meta_thread_watcher_get_instance_private (watcher); -+ struct itimerspec timer_interval; -+ int ret; -+ -+ g_return_val_if_fail (META_IS_THREAD_WATCHER (watcher), FALSE); -+ -+ timer_interval.it_value.tv_sec = 0; -+ timer_interval.it_value.tv_nsec = ms2ns (priv->interval_ms); -+ timer_interval.it_interval.tv_sec = timer_interval.it_value.tv_sec; -+ timer_interval.it_interval.tv_nsec = timer_interval.it_value.tv_nsec; -+ -+ ret = timer_settime (*priv->timer, 0, &timer_interval, NULL); -+ -+ if (ret == -1) -+ { -+ g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), -+ "Failed to arm unix timer: %s", g_strerror (errno)); -+ meta_thread_watcher_stop (watcher); -+ -+ return FALSE; -+ } -+ -+ return TRUE; -+} -+ -+void -+meta_thread_watcher_stop (MetaThreadWatcher *watcher) -+{ -+ MetaThreadWatcherPrivate *priv = meta_thread_watcher_get_instance_private (watcher); -+ -+ g_return_if_fail (META_IS_THREAD_WATCHER (watcher)); -+ -+ if (!meta_thread_watcher_is_started (watcher)) -+ return; -+ -+ meta_thread_watcher_detach (watcher); -+ -+ g_clear_pointer (&priv->timer, free_timer); -+ g_clear_handle_id (&priv->notification_watch_id, g_source_remove); -+ close_fds (watcher); -+} -diff --git a/src/backends/native/meta-thread-watcher.h b/src/backends/native/meta-thread-watcher.h -new file mode 100644 -index 000000000..fa6a1e4c8 ---- /dev/null -+++ b/src/backends/native/meta-thread-watcher.h -@@ -0,0 +1,36 @@ -+/* -+ * Copyright (C) 2023 Red Hat -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License as -+ * published by the Free Software Foundation; either version 2 of the -+ * License, or (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, but -+ * WITHOUT ANY WARRANTY; without even the watcheried warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program; if not, see . -+ */ -+ -+#pragma once -+ -+#include -+ -+#define META_TYPE_THREAD_WATCHER (meta_thread_watcher_get_type ()) -+G_DECLARE_FINAL_TYPE (MetaThreadWatcher, meta_thread_watcher, -+ META, THREAD_WATCHER, GObject) -+ -+MetaThreadWatcher *meta_thread_watcher_new (void); -+void meta_thread_watcher_attach (MetaThreadWatcher *self, -+ GMainContext *context); -+void meta_thread_watcher_detach (MetaThreadWatcher *self); -+gboolean meta_thread_watcher_start (MetaThreadWatcher *watcher, -+ int interval_us, -+ GError **error); -+gboolean meta_thread_watcher_is_started (MetaThreadWatcher *watcher); -+gboolean meta_thread_watcher_reset (MetaThreadWatcher *watcher, -+ GError **error); -+void meta_thread_watcher_stop (MetaThreadWatcher *watcher); -diff --git a/src/backends/native/meta-thread.c b/src/backends/native/meta-thread.c -index 08d01144d..a4188ee56 100644 ---- a/src/backends/native/meta-thread.c -+++ b/src/backends/native/meta-thread.c -@@ -1,116 +1,121 @@ - /* - * Copyright (C) 2018-2021 Red Hat - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - */ - - #include "config.h" - - #include "backends/native/meta-thread-private.h" - - #include -+#include -+#include - #include -+#include - - #include "backends/meta-backend-private.h" - #include "backends/meta-backend-types.h" - #include "backends/native/meta-thread-impl.h" -+#include "backends/native/meta-thread-watcher.h" - - #include "meta-dbus-rtkit1.h" - #include "meta-private-enum-types.h" - - enum - { - PROP_0, - - PROP_BACKEND, - PROP_NAME, - PROP_THREAD_TYPE, - PROP_WANTS_REALTIME, - - N_PROPS - }; - - static GParamSpec *obj_props[N_PROPS]; - - typedef struct _MetaThreadCallbackData - { - MetaThreadCallback callback; - gpointer user_data; - GDestroyNotify user_data_destroy; - } MetaThreadCallbackData; - - typedef struct _MetaThreadCallbackSource - { - GSource base; - - GMutex mutex; - GCond cond; - - MetaThread *thread; - GMainContext *main_context; - GList *callbacks; - gboolean needs_flush; - } MetaThreadCallbackSource; - - typedef struct _MetaThreadPrivate - { - MetaBackend *backend; - char *name; - - GMainContext *main_context; - - MetaThreadImpl *impl; - gboolean wants_realtime; - gboolean waiting_for_impl_task; - GSource *wrapper_source; - - GMutex callbacks_mutex; - GHashTable *callback_sources; - - MetaThreadType thread_type; - - GThread *main_thread; - - struct { - GThread *thread; -+ MetaThreadWatcher *thread_watcher; - GMutex init_mutex; - } kernel; - } MetaThreadPrivate; - - typedef struct _MetaThreadClassPrivate - { - GType impl_type; - } MetaThreadClassPrivate; - - static void initable_iface_init (GInitableIface *initable_iface); - - G_DEFINE_TYPE_WITH_CODE (MetaThread, meta_thread, G_TYPE_OBJECT, - G_ADD_PRIVATE (MetaThread) - G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, - initable_iface_init) - g_type_add_class_private (g_define_type_id, - sizeof (MetaThreadClassPrivate))) - - static void - meta_thread_callback_data_free (MetaThreadCallbackData *callback_data) - { - if (callback_data->user_data_destroy) - callback_data->user_data_destroy (callback_data->user_data); - g_free (callback_data); - } - - static void - meta_thread_get_property (GObject *object, - guint prop_id, - GValue *value, -@@ -181,123 +186,137 @@ get_rtkit_property (MetaDBusRealtimeKit1 *rtkit_proxy, - * org.freedesktop.DBus.Properties.GetAll. See - * . - */ - connection = g_dbus_proxy_get_connection (G_DBUS_PROXY (rtkit_proxy)); - prop_value = - g_dbus_connection_call_sync (connection, - "org.freedesktop.RealtimeKit1", - "/org/freedesktop/RealtimeKit1", - "org.freedesktop.DBus.Properties", - "Get", - g_variant_new ("(ss)", - "org.freedesktop.RealtimeKit1", - property_name), - G_VARIANT_TYPE ("(v)"), - G_DBUS_CALL_FLAGS_NO_AUTO_START, - -1, NULL, error); - if (!prop_value) - return NULL; - - g_variant_get (prop_value, "(v)", &property_variant); - return g_steal_pointer (&property_variant); - } - - static gboolean - request_real_time_scheduling (MetaThread *thread, - GError **error) - { - MetaThreadPrivate *priv = meta_thread_get_instance_private (thread); - g_autoptr (MetaDBusRealtimeKit1) rtkit_proxy = NULL; - g_autoptr (GError) local_error = NULL; -- int64_t rttime; -+ int64_t rttime, watch_interval; - struct rlimit rl; - uint32_t priority; - - rtkit_proxy = - meta_dbus_realtime_kit1_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, - G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS | - G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, - "org.freedesktop.RealtimeKit1", - "/org/freedesktop/RealtimeKit1", - NULL, - &local_error); - if (!rtkit_proxy) - { - g_dbus_error_strip_remote_error (local_error); - g_propagate_prefixed_error (error, g_steal_pointer (&local_error), - "Failed to acquire RTKit D-Bus proxy: "); - return FALSE; - } - - priority = meta_dbus_realtime_kit1_get_max_realtime_priority (rtkit_proxy); - if (priority == 0) - { - g_autoptr (GVariant) priority_variant = NULL; - - priority_variant = get_rtkit_property (rtkit_proxy, - "MaxRealtimePriority", - error); - if (!priority_variant) - return FALSE; - - priority = g_variant_get_int32 (priority_variant); - } - - if (priority == 0) - g_warning ("Maximum real time scheduling priority is 0"); - - rttime = meta_dbus_realtime_kit1_get_rttime_usec_max (rtkit_proxy); - if (rttime == 0) - { - g_autoptr (GVariant) rttime_variant = NULL; - - rttime_variant = get_rtkit_property (rtkit_proxy, - "RTTimeUSecMax", - error); - if (!rttime_variant) - return FALSE; - - rttime = g_variant_get_int64 (rttime_variant); - } - - meta_topic (META_DEBUG_BACKEND, - "Setting soft and hard RLIMIT_RTTIME limit to %lu", rttime); -+ -+ /* We set the soft-limit and hard-limit to the same value so the -+ * kernel won't send SIGXCPU to random threads. We synthesize our -+ * own SIGXCPU with a timer (See MetaThreadWatcher) that's always -+ * delivered to the approprate thread. -+ */ - rl.rlim_cur = rttime; - rl.rlim_max = rttime; - - if (setrlimit (RLIMIT_RTTIME, &rl) != 0) - { - g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), - "Failed to set RLIMIT_RTTIME: %s", g_strerror (errno)); - return FALSE; - } - -+ /* We make sure if a SIGXCPU is synthesized it gets raised before anything -+ * the kernel could throw at us. We do this by setting an interval three quarters -+ * of the soft limit. -+ */ -+ watch_interval = (rl.rlim_cur * 3) / 4; -+ if (!meta_thread_watcher_start (priv->kernel.thread_watcher, watch_interval, error)) -+ return FALSE; -+ - meta_topic (META_DEBUG_BACKEND, "Setting '%s' thread real time priority to %d", - priv->name, priority); - if (!meta_dbus_realtime_kit1_call_make_thread_realtime_sync (rtkit_proxy, - gettid (), - priority, - NULL, - &local_error)) - { - g_dbus_error_strip_remote_error (local_error); - g_propagate_error (error, g_steal_pointer (&local_error)); - return FALSE; - } - - return TRUE; - } - - static gpointer - thread_impl_func (gpointer user_data) - { - MetaThread *thread = META_THREAD (user_data); - MetaThreadPrivate *priv = meta_thread_get_instance_private (thread); - MetaThreadImpl *impl = priv->impl; - MetaThreadImplRunFlags run_flags = META_THREAD_IMPL_RUN_FLAG_NONE; - GMainContext *thread_context = meta_thread_impl_get_main_context (impl); - #ifdef HAVE_PROFILER - MetaContext *context = meta_backend_get_context (priv->backend); - MetaProfiler *profiler = meta_context_get_profiler (context); - #endif - - g_mutex_lock (&priv->kernel.init_mutex); -@@ -443,77 +462,106 @@ static void - wrap_main_context (MetaThread *thread, - GMainContext *thread_main_context) - { - MetaThreadPrivate *priv = meta_thread_get_instance_private (thread); - g_autoptr (GSource) source = NULL; - WrapperSource *wrapper_source; - - if (!g_main_context_acquire (thread_main_context)) - g_return_if_reached (); - - source = g_source_new (&wrapper_source_funcs, - sizeof (WrapperSource)); - wrapper_source = (WrapperSource *) source; - wrapper_source->thread_main_context = thread_main_context; - g_source_set_ready_time (source, -1); - g_source_attach (source, NULL); - - priv->wrapper_source = source; - } - - static void - unwrap_main_context (MetaThread *thread, - GMainContext *thread_main_context) - { - MetaThreadPrivate *priv = meta_thread_get_instance_private (thread); - - g_main_context_release (thread_main_context); - g_clear_pointer (&priv->wrapper_source, g_source_destroy); - } - -+static void -+on_realtime_thread_stalled (MetaThread *thread) -+{ -+ MetaThreadPrivate *priv = meta_thread_get_instance_private (thread); -+ -+ g_warning ("Disabling realtime scheduling"); -+ priv->wants_realtime = FALSE; -+ meta_thread_reset_thread_type (thread, META_THREAD_TYPE_KERNEL); -+} -+ - static void - start_thread (MetaThread *thread) - { - MetaThreadPrivate *priv = meta_thread_get_instance_private (thread); -+ g_autoptr (GError) error = NULL; - - switch (priv->thread_type) - { - case META_THREAD_TYPE_USER: - wrap_main_context (thread, - meta_thread_impl_get_main_context (priv->impl)); - break; - case META_THREAD_TYPE_KERNEL: - g_mutex_init (&priv->kernel.init_mutex); - g_mutex_lock (&priv->kernel.init_mutex); -+ -+ if (priv->wants_realtime) -+ { -+ GMainContext *main_context; -+ -+ priv->kernel.thread_watcher = meta_thread_watcher_new (); -+ main_context = meta_thread_impl_get_main_context (priv->impl); -+ -+ meta_thread_watcher_attach (priv->kernel.thread_watcher, main_context); -+ -+ g_signal_connect_object (priv->kernel.thread_watcher, -+ "thread-stalled", -+ G_CALLBACK (on_realtime_thread_stalled), -+ thread, -+ G_CONNECT_SWAPPED); -+ } -+ - priv->kernel.thread = g_thread_new (priv->name, - thread_impl_func, - thread); -+ - g_mutex_unlock (&priv->kernel.init_mutex); - break; - } - } - - static gboolean - meta_thread_initable_init (GInitable *initable, - GCancellable *cancellable, - GError **error) - { - MetaThread *thread = META_THREAD (initable); - MetaThreadPrivate *priv = meta_thread_get_instance_private (thread); - MetaThreadClass *thread_class = META_THREAD_GET_CLASS (thread); - MetaThreadClassPrivate *class_priv = - G_TYPE_CLASS_GET_PRIVATE (thread_class, META_TYPE_THREAD, - MetaThreadClassPrivate); - g_autoptr (GMainContext) thread_context = NULL; - - priv->main_context = g_main_context_default (); - - priv->callback_sources = - g_hash_table_new_full (NULL, NULL, - NULL, (GDestroyNotify) g_source_destroy); - meta_thread_register_callback_context (thread, priv->main_context); - - thread_context = g_main_context_new (); - - g_assert (g_type_is_a (class_priv->impl_type, META_TYPE_THREAD_IMPL)); - priv->impl = g_object_new (class_priv->impl_type, - "thread", thread, -@@ -522,60 +570,63 @@ meta_thread_initable_init (GInitable *initable, - - start_thread (thread); - - return TRUE; - } - - static void - initable_iface_init (GInitableIface *initable_iface) - { - initable_iface->init = meta_thread_initable_init; - } - - static void - finalize_thread_user (MetaThread *thread) - { - MetaThreadPrivate *priv = meta_thread_get_instance_private (thread); - - meta_thread_impl_terminate (priv->impl); - while (meta_thread_impl_dispatch (priv->impl) > 0); - unwrap_main_context (thread, meta_thread_impl_get_main_context (priv->impl)); - } - - static void - finalize_thread_kernel (MetaThread *thread) - { - MetaThreadPrivate *priv = meta_thread_get_instance_private (thread); - - meta_thread_impl_terminate (priv->impl); - g_thread_join (priv->kernel.thread); - priv->kernel.thread = NULL; -+ -+ g_clear_object (&priv->kernel.thread_watcher); -+ - g_mutex_clear (&priv->kernel.init_mutex); - } - - static void - tear_down_thread (MetaThread *thread) - { - MetaThreadPrivate *priv = meta_thread_get_instance_private (thread); - - switch (priv->thread_type) - { - case META_THREAD_TYPE_USER: - finalize_thread_user (thread); - break; - case META_THREAD_TYPE_KERNEL: - finalize_thread_kernel (thread); - break; - } - - meta_thread_flush_callbacks (thread); - } - - static void - meta_thread_finalize (GObject *object) - { - MetaThread *thread = META_THREAD (object); - MetaThreadPrivate *priv = meta_thread_get_instance_private (thread); - - tear_down_thread (thread); - - meta_thread_unregister_callback_context (thread, priv->main_context); -@@ -640,61 +691,71 @@ meta_thread_class_init (MetaThreadClass *klass) - - static void - meta_thread_init (MetaThread *thread) - { - MetaThreadPrivate *priv = meta_thread_get_instance_private (thread); - - g_mutex_init (&priv->callbacks_mutex); - priv->main_thread = g_thread_self (); - } - - void - meta_thread_class_register_impl_type (MetaThreadClass *thread_class, - GType impl_type) - { - MetaThreadClassPrivate *class_priv = - G_TYPE_CLASS_GET_PRIVATE (thread_class, META_TYPE_THREAD, - MetaThreadClassPrivate); - - g_assert (class_priv->impl_type == G_TYPE_INVALID); - class_priv->impl_type = impl_type; - } - - void - meta_thread_reset_thread_type (MetaThread *thread, - MetaThreadType thread_type) - { - MetaThreadPrivate *priv = meta_thread_get_instance_private (thread); - g_autoptr (GMainContext) thread_context = NULL; - - if (priv->thread_type == thread_type) -- return; -+ { -+ gboolean is_realtime; -+ -+ if (thread_type != META_THREAD_TYPE_KERNEL) -+ return; -+ -+ is_realtime = meta_thread_impl_is_realtime (priv->impl); -+ -+ if (is_realtime == priv->wants_realtime) -+ return; -+ } - - tear_down_thread (thread); - g_assert (!priv->wrapper_source); - - priv->thread_type = thread_type; - - start_thread (thread); - - switch (priv->thread_type) - { - case META_THREAD_TYPE_USER: - g_assert (priv->wrapper_source); - break; - case META_THREAD_TYPE_KERNEL: - g_assert (!priv->wrapper_source); - break; - } - } - - static int - dispatch_callbacks (MetaThread *thread, - GList *pending_callbacks) - { - int callback_count = 0; - GList *l; - - for (l = pending_callbacks; l; l = l->next) - { - MetaThreadCallbackData *callback_data = l->data; - -diff --git a/src/meson.build b/src/meson.build -index ea3614936..297aa8302 100644 ---- a/src/meson.build -+++ b/src/meson.build -@@ -828,61 +828,62 @@ if have_native_backend - 'backends/native/meta-kms.h', - 'backends/native/meta-onscreen-native.c', - 'backends/native/meta-onscreen-native.h', - 'backends/native/meta-pointer-constraint-native.c', - 'backends/native/meta-pointer-constraint-native.h', - 'backends/native/meta-render-device-gbm.c', - 'backends/native/meta-render-device-gbm.h', - 'backends/native/meta-render-device-private.h', - 'backends/native/meta-render-device-surfaceless.c', - 'backends/native/meta-render-device-surfaceless.h', - 'backends/native/meta-render-device.c', - 'backends/native/meta-render-device.h', - 'backends/native/meta-renderer-native-gles3.c', - 'backends/native/meta-renderer-native-gles3.h', - 'backends/native/meta-renderer-native-private.h', - 'backends/native/meta-renderer-native.c', - 'backends/native/meta-renderer-native.h', - 'backends/native/meta-renderer-view-native.c', - 'backends/native/meta-renderer-view-native.h', - 'backends/native/meta-seat-impl.c', - 'backends/native/meta-seat-impl.h', - 'backends/native/meta-seat-native.c', - 'backends/native/meta-seat-native.h', - 'backends/native/meta-stage-native.c', - 'backends/native/meta-stage-native.h', - 'backends/native/meta-thread-impl.c', - 'backends/native/meta-thread-impl.h', - 'backends/native/meta-thread-private.h', - 'backends/native/meta-thread.c', - 'backends/native/meta-thread.h', -- 'backends/native/meta-thread-private.h', -+ 'backends/native/meta-thread-watcher.h', -+ 'backends/native/meta-thread-watcher.c', - 'backends/native/meta-udev.c', - 'backends/native/meta-udev.h', - 'backends/native/meta-virtual-input-device-native.c', - 'backends/native/meta-virtual-input-device-native.h', - 'backends/native/meta-virtual-monitor-native.c', - 'backends/native/meta-virtual-monitor-native.h', - 'backends/native/meta-xkb-utils.c', - 'backends/native/meta-xkb-utils.h', - 'compositor/meta-compositor-native.c', - 'compositor/meta-compositor-native.h', - 'compositor/meta-compositor-view-native.c', - 'compositor/meta-compositor-view-native.h', - ] - endif - - if have_wayland or have_native_backend - mutter_sources += [ - 'common/meta-cogl-drm-formats.c', - 'common/meta-cogl-drm-formats.h', - ] - endif - - if have_wayland_eglstream - mutter_sources += [ - 'wayland/meta-wayland-egl-stream.c', - 'wayland/meta-wayland-egl-stream.h', - ] - endif - - mutter_private_enum_sources = [ --- -2.41.0 - diff --git a/mutter.spec b/mutter.spec index 990bd84..af955f9 100644 --- a/mutter.spec +++ b/mutter.spec @@ -32,10 +32,6 @@ Patch: 0001-place-Always-center-initial-setup-fedora-welcome.patch Patch: 0001-gschema-Enable-scale-monitor-framebuffer-experimenta.patch -Patch: 0001-util-Add-way-to-print-backtraces.patch -Patch: 0002-clutter-Add-ms2ns-helper.patch -Patch: 0003-native-Stop-using-real-time-thread-if-it-stalls.patch - # https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3299 Patch: 3299.patch @@ -55,7 +51,6 @@ Patch: 0003-thread-Allow-turnning-off-rt-scheduling-for-running-.patch Patch: 0004-kms-impl-device-Inhibit-real-time-scheduling-when-mo.patch BuildRequires: pkgconfig(gobject-introspection-1.0) >= 1.41.0 -BuildRequires: pkgconfig(polkit-gobject-1) BuildRequires: pkgconfig(sm) BuildRequires: pkgconfig(libwacom) BuildRequires: pkgconfig(x11) @@ -198,9 +193,6 @@ the functionality of the installed %{name} package. %{_libdir}/mutter-%{mutter_api_version}/ %{_libexecdir}/mutter-restart-helper %{_libexecdir}/mutter-x11-frames -%{_libexecdir}/mutter-backtrace -%{_datadir}/polkit-1/actions/org.gnome.mutter.backtrace.policy -%{_datadir}/polkit-1/rules.d/50-mutter.rules %{_mandir}/man1/mutter.1* %files common