gvfs/trash-Add-worker-thread.patch
Ondrej Holy b57f602cb4 Improve trash backend performance
Resolves: RHEL-127445
2026-06-03 12:30:27 +02:00

296 lines
8.9 KiB
Diff

From 890b2a747bbcd9ed220fb217aba41b9ef0188ba1 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Wed, 7 Jun 2023 15:49:41 +0200
Subject: [PATCH] trash: Add worker thread
The previous commit makes the backend a bit more resilience of against
stale NFS mounts, but still a lot of I/O operations (e.g.
`trash_watcher_rescan`) run on the main thread and can easily block the
whole backend. This is especially problem for the `create_dir_monitor` and
`create_file_monitor` methods that doesn't have asynchronous variants.
This may cause hangs for nautilus, or file chooser dialog, but also for
the whole gnome-shell session (resp. desktop-icons extension). Let's add
a worker thread to ensure that the `create_dir_monitor` and
`create_file_monitor` methods response immediately.
Related: https://bugzilla.redhat.com/show_bug.cgi?id=2152538
---
daemon/gvfsbackendtrash.c | 156 ++++++++++++++++++++++++++++++++------
1 file changed, 131 insertions(+), 25 deletions(-)
diff --git a/daemon/gvfsbackendtrash.c b/daemon/gvfsbackendtrash.c
index b4d3d089..0966242a 100644
--- a/daemon/gvfsbackendtrash.c
+++ b/daemon/gvfsbackendtrash.c
@@ -36,6 +36,10 @@ struct OPAQUE_TYPE__GVfsBackendTrash
GVfsMonitor *file_monitor;
GVfsMonitor *dir_monitor;
+ GMainContext *worker_context;
+ GMainLoop *worker_loop;
+ GThread *worker_thread;
+
TrashWatcher *watcher;
TrashRoot *root;
@@ -44,6 +48,94 @@ struct OPAQUE_TYPE__GVfsBackendTrash
G_DEFINE_TYPE (GVfsBackendTrash, g_vfs_backend_trash, G_VFS_TYPE_BACKEND);
+typedef struct
+{
+ GSourceFunc source_func;
+ gpointer user_data;
+ GMutex mutex;
+ GCond cond;
+ gboolean completed;
+} ContextInvokeData;
+
+static gboolean
+source_func_wrapper (gpointer user_data)
+{
+ ContextInvokeData *data = user_data;
+
+ g_mutex_lock (&data->mutex);
+
+ while (data->source_func (data->user_data));
+ data->completed = TRUE;
+
+ g_cond_signal (&data->cond);
+ g_mutex_unlock (&data->mutex);
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+trash_backend_worker_thread_queue_and_wait (GVfsBackendTrash *backend,
+ GSourceFunc source_func)
+{
+ ContextInvokeData data;
+
+ data.source_func = source_func;
+ data.user_data = backend;
+
+ g_mutex_init (&data.mutex);
+ g_cond_init (&data.cond);
+ data.completed = FALSE;
+
+ g_mutex_lock (&data.mutex);
+
+ g_main_context_invoke (backend->worker_context,
+ source_func_wrapper,
+ &data);
+
+ while (!data.completed)
+ {
+ g_cond_wait (&data.cond, &data.mutex);
+ }
+
+ g_mutex_unlock (&data.mutex);
+
+ g_mutex_clear (&data.mutex);
+ g_cond_clear (&data.cond);
+}
+
+static void
+trash_backend_worker_thread_queue (GVfsBackendTrash *backend,
+ GSourceFunc source_func)
+{
+ g_main_context_invoke (backend->worker_context, source_func, backend);
+}
+
+static gboolean
+watch_func (gpointer user_data)
+{
+ GVfsBackendTrash *backend = G_VFS_BACKEND_TRASH (user_data);
+
+ trash_watcher_watch (backend->watcher);
+
+ return G_SOURCE_REMOVE;
+}
+
+static gboolean
+rescan_func (gpointer user_data)
+{
+ GVfsBackendTrash *backend = G_VFS_BACKEND_TRASH (user_data);
+
+ trash_watcher_rescan (backend->watcher);
+
+ return G_SOURCE_REMOVE;
+}
+
+static gboolean
+ready_func (gpointer user_data)
+{
+ return G_SOURCE_REMOVE;
+}
+
static gboolean
is_root (const char *filename)
{
@@ -63,7 +155,7 @@ trash_backend_get_file_monitor (GVfsBackendTrash *backend,
* no possibility here for creating more than one new monitor.
*/
if (backend->dir_monitor == NULL)
- trash_watcher_watch (backend->watcher);
+ trash_backend_worker_thread_queue (backend, watch_func);
backend->file_monitor = g_vfs_monitor_new (G_VFS_BACKEND (backend));
}
@@ -84,7 +176,7 @@ trash_backend_get_dir_monitor (GVfsBackendTrash *backend,
* no possibility here for creating more than one new monitor.
*/
if (backend->file_monitor == NULL)
- trash_watcher_watch (backend->watcher);
+ trash_backend_worker_thread_queue (backend, watch_func);
backend->dir_monitor = g_vfs_monitor_new (G_VFS_BACKEND (backend));
}
@@ -164,7 +256,6 @@ trash_backend_item_count_changed (gpointer user_data)
}
}
-
static GFile *
trash_backend_get_file (GVfsBackendTrash *backend,
const char *filename,
@@ -177,6 +268,8 @@ trash_backend_get_file (GVfsBackendTrash *backend,
TrashItem *item;
GFile *file;
+ trash_backend_worker_thread_queue_and_wait (backend, rescan_func);
+
file = NULL;
filename++;
@@ -243,9 +336,6 @@ trash_backend_open_for_read (GVfsBackend *vfs_backend,
{
GFile *real;
- if (!backend->file_monitor && !backend->dir_monitor)
- trash_watcher_rescan (backend->watcher);
-
real = trash_backend_get_file (backend, filename, NULL, NULL, &error);
if (real)
@@ -404,9 +494,6 @@ trash_backend_delete (GVfsBackend *vfs_backend,
TrashItem *item;
GFile *real;
- if (!backend->file_monitor && !backend->dir_monitor)
- trash_watcher_rescan (backend->watcher);
-
real = trash_backend_get_file (backend, filename,
&item, &is_toplevel, &error);
@@ -461,9 +548,6 @@ trash_backend_pull (GVfsBackend *vfs_backend,
TrashItem *item;
GFile *real;
- if (!backend->file_monitor && !backend->dir_monitor)
- trash_watcher_rescan (backend->watcher);
-
real = trash_backend_get_file (backend, source, &item,
&is_toplevel, &error);
@@ -585,6 +669,8 @@ trash_backend_enumerate_root (GVfsBackendTrash *backend,
g_vfs_job_succeeded (G_VFS_JOB (job));
+ trash_backend_worker_thread_queue_and_wait (backend, rescan_func);
+
items = trash_root_get_items (backend->root);
for (node = items; node; node = node->next)
@@ -675,8 +761,6 @@ trash_backend_enumerate (GVfsBackend *vfs_backend,
g_assert (filename[0] == '/');
- trash_watcher_rescan (backend->watcher);
-
if (!is_root (filename))
trash_backend_enumerate_non_root (backend, job, filename,
attribute_matcher, flags);
@@ -684,6 +768,31 @@ trash_backend_enumerate (GVfsBackend *vfs_backend,
trash_backend_enumerate_root (backend, job, attribute_matcher, flags);
}
+static gpointer
+thread_func (gpointer user_data)
+{
+ GVfsBackendTrash *backend = G_VFS_BACKEND_TRASH (user_data);
+
+ g_main_context_push_thread_default (backend->worker_context);
+ backend->worker_loop = g_main_loop_new (backend->worker_context, FALSE);
+
+ backend->root = trash_root_new (trash_backend_item_created,
+ trash_backend_item_deleted,
+ trash_backend_item_count_changed,
+ backend);
+ backend->watcher = trash_watcher_new (backend->root);
+
+ g_main_loop_run (backend->worker_loop);
+
+ trash_watcher_free (backend->watcher);
+ trash_root_free (backend->root);
+
+ g_main_context_pop_thread_default (backend->worker_context);
+ g_main_loop_unref (backend->worker_loop);
+
+ return NULL;
+}
+
static void
trash_backend_mount (GVfsBackend *vfs_backend,
GVfsJobMount *job,
@@ -695,11 +804,12 @@ trash_backend_mount (GVfsBackend *vfs_backend,
backend->file_monitor = NULL;
backend->dir_monitor = NULL;
- backend->root = trash_root_new (trash_backend_item_created,
- trash_backend_item_deleted,
- trash_backend_item_count_changed,
- backend);
- backend->watcher = trash_watcher_new (backend->root);
+
+ backend->worker_context = g_main_context_new ();
+ backend->worker_thread = g_thread_new ("Trash Worker Thread",
+ thread_func,
+ backend);
+ trash_backend_worker_thread_queue_and_wait (backend, ready_func);
g_vfs_job_succeeded (G_VFS_JOB (job));
}
@@ -716,9 +826,6 @@ trash_backend_query_info (GVfsBackend *vfs_backend,
g_assert (filename[0] == '/');
- if (!backend->file_monitor && !backend->dir_monitor)
- trash_watcher_rescan (backend->watcher);
-
if (!is_root (filename))
{
GError *error = NULL;
@@ -762,6 +869,8 @@ trash_backend_query_info (GVfsBackend *vfs_backend,
GIcon *icon;
int n_items;
+ trash_backend_worker_thread_queue_and_wait (backend, rescan_func);
+
n_items = trash_root_get_n_items (backend->root);
g_file_info_set_file_type (info, G_FILE_TYPE_DIRECTORY);
@@ -878,9 +987,6 @@ trash_backend_finalize (GObject *object)
if (backend->dir_monitor)
g_object_unref (backend->dir_monitor);
backend->dir_monitor = NULL;
-
- trash_watcher_free (backend->watcher);
- trash_root_free (backend->root);
}
static void
--
2.53.0