Blame SOURCES/0004-global-force-fsync-to-worker-thread-when-saving-stat.patch

1be690
From 2a4f33df723d4b9ce68e5948b568a89675d37411 Mon Sep 17 00:00:00 2001
e2a246
From: Christian Hergert <chergert@redhat.com>
e2a246
Date: Wed, 26 Feb 2020 14:46:20 -0800
1be690
Subject: [PATCH 4/6] global: force fsync() to worker thread when saving state
e2a246
e2a246
The g_file_replace_contents_async() API can potentially call fsync() from
e2a246
the thread calling into it upon completion. This can have disasterous
e2a246
effects when run from the compositor main thread such as complete stalls.
e2a246
e2a246
This is a followup to 86a00b6872375a266449beee1ea6d5e94f1ebbcb which
e2a246
assumed (like the rest of us) that the fsync() would be performed on the
e2a246
thread that was doing the I/O operations.
e2a246
e2a246
You can verify this with an strace -e fsync and cause terminal to display
e2a246
a command completed notification (eg: from a backdrop window).
e2a246
e2a246
This also fixes a lifecycle bug for the variant, as
e2a246
g_file_replace_contents_async() does not copy the data during the operation
e2a246
as that is the responsibility of the caller. Instead, we just use a GBytes
e2a246
variant and reference the variant there.
e2a246
e2a246
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/1050
e2a246
---
e2a246
 src/shell-global.c | 70 +++++++++++++++++++++++++++++++++++++++++-----
e2a246
 1 file changed, 63 insertions(+), 7 deletions(-)
e2a246
e2a246
diff --git a/src/shell-global.c b/src/shell-global.c
e2a246
index df84b6b0d..4b33778e0 100644
e2a246
--- a/src/shell-global.c
e2a246
+++ b/src/shell-global.c
e2a246
@@ -1572,6 +1572,55 @@ delete_variant_cb (GObject      *object,
e2a246
   g_hash_table_remove (global->save_ops, object);
e2a246
 }
e2a246
 
e2a246
+static void
e2a246
+replace_contents_worker (GTask        *task,
e2a246
+                         gpointer      source_object,
e2a246
+                         gpointer      task_data,
e2a246
+                         GCancellable *cancellable)
e2a246
+{
e2a246
+  GFile *file = source_object;
e2a246
+  GBytes *bytes = task_data;
e2a246
+  GError *error = NULL;
e2a246
+  const gchar *data;
e2a246
+  gsize len;
e2a246
+
e2a246
+  data = g_bytes_get_data (bytes, &len;;
e2a246
+
e2a246
+  if (!g_file_replace_contents (file, data, len, NULL, FALSE,
e2a246
+                                G_FILE_CREATE_REPLACE_DESTINATION,
e2a246
+                                NULL, cancellable, &error))
e2a246
+    g_task_return_error (task, g_steal_pointer (&error));
e2a246
+  else
e2a246
+    g_task_return_boolean (task, TRUE);
e2a246
+}
e2a246
+
e2a246
+static void
e2a246
+replace_contents_async (GFile               *path,
e2a246
+                        GBytes              *bytes,
e2a246
+                        GCancellable        *cancellable,
e2a246
+                        GAsyncReadyCallback  callback,
e2a246
+                        gpointer             user_data)
e2a246
+{
e2a246
+  g_autoptr(GTask) task = NULL;
e2a246
+
e2a246
+  g_assert (G_IS_FILE (path));
e2a246
+  g_assert (bytes != NULL);
e2a246
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
e2a246
+
e2a246
+  task = g_task_new (path, cancellable, callback, user_data);
e2a246
+  g_task_set_source_tag (task, replace_contents_async);
e2a246
+  g_task_set_task_data (task, g_bytes_ref (bytes), (GDestroyNotify)g_bytes_unref);
e2a246
+  g_task_run_in_thread (task, replace_contents_worker);
e2a246
+}
e2a246
+
e2a246
+static gboolean
e2a246
+replace_contents_finish (GFile         *file,
e2a246
+                         GAsyncResult  *result,
e2a246
+                         GError       **error)
e2a246
+{
e2a246
+  return g_task_propagate_boolean (G_TASK (result), error);
e2a246
+}
e2a246
+
e2a246
 static void
e2a246
 replace_variant_cb (GObject      *object,
e2a246
                     GAsyncResult *result,
e2a246
@@ -1580,7 +1629,7 @@ replace_variant_cb (GObject      *object,
e2a246
   ShellGlobal *global = user_data;
e2a246
   GError *error = NULL;
e2a246
 
e2a246
-  if (!g_file_replace_contents_finish (G_FILE (object), result, NULL, &error))
e2a246
+  if (!replace_contents_finish (G_FILE (object), result, &error))
e2a246
     {
e2a246
       if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
e2a246
         {
e2a246
@@ -1616,12 +1665,19 @@ save_variant (ShellGlobal *global,
e2a246
     }
e2a246
   else
e2a246
     {
e2a246
-      g_file_replace_contents_async (path,
e2a246
-                                     g_variant_get_data (variant),
e2a246
-                                     g_variant_get_size (variant),
e2a246
-                                     NULL, FALSE,
e2a246
-                                     G_FILE_CREATE_REPLACE_DESTINATION,
e2a246
-                                     cancellable, replace_variant_cb, global);
e2a246
+      g_autoptr(GBytes) bytes = NULL;
e2a246
+
e2a246
+      bytes = g_bytes_new_with_free_func (g_variant_get_data (variant),
e2a246
+                                          g_variant_get_size (variant),
e2a246
+                                          (GDestroyNotify)g_variant_unref,
e2a246
+                                          g_variant_ref (variant));
e2a246
+      /* g_file_replace_contents_async() can potentially fsync() from the
e2a246
+       * calling thread when completing the asynchronous task. Instead, we
e2a246
+       * want to force that fsync() to a thread to avoid blocking the
e2a246
+       * compository main loop. Using our own replace_contents_async()
e2a246
+       * simply executes the operation synchronously from a thread.
e2a246
+       */
e2a246
+      replace_contents_async (path, bytes, cancellable, replace_variant_cb, global);
e2a246
     }
e2a246
 
e2a246
   g_object_unref (path);
e2a246
-- 
1be690
2.26.2
e2a246