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