From 8b9bf6b0514149859595420c77cad648599f1d9f Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Mon, 29 Jul 2024 11:29:48 +0200 Subject: [PATCH] core: Add async save method to MetaSessionManager Allow saving the session gvdb file in the background, with as little overhead in the main thread as possible. We still need to serialize all created/deserialized MetaSessionState to a GVDB hashtable there, in order to avoid these being poked from the async task thread. Part-of: --- src/core/meta-session-manager.c | 166 ++++++++++++++++++++++++++------ src/core/meta-session-manager.h | 8 ++ 2 files changed, 146 insertions(+), 28 deletions(-) diff --git a/src/core/meta-session-manager.c b/src/core/meta-session-manager.c index 3b6715798..3270fd788 100644 --- a/src/core/meta-session-manager.c +++ b/src/core/meta-session-manager.c @@ -31,9 +31,20 @@ #define SESSION_FILE_NAME "session.gvdb" +typedef struct _MetaSessionData MetaSessionData; + +struct _MetaSessionData +{ + const char *name; + GHashTable *new_table; /* Gvdb table */ + GHashTable *deleted_sessions; /* Set of session names */ + GvdbTable *gvdb_table; +}; + struct _MetaSessionManager { GObject parent_instance; + GMutex mutex; GHashTable *sessions; /* Session name -> MetaSessionState */ GHashTable *deleted_sessions; /* Set of session names */ GvdbTable *gvdb_table; @@ -116,6 +127,7 @@ meta_session_manager_finalize (GObject *object) g_clear_pointer (&session_manager->gvdb_table, gvdb_table_free); g_clear_pointer (&session_manager->name, g_free); g_clear_fd (&session_manager->fd, NULL); + g_mutex_clear (&session_manager->mutex); G_OBJECT_CLASS (meta_session_manager_parent_class)->finalize (object); } @@ -151,6 +163,7 @@ meta_session_manager_init (MetaSessionManager *session_manager) g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref); session_manager->deleted_sessions = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + g_mutex_init (&session_manager->mutex); } static gboolean @@ -350,16 +363,21 @@ snapshot_gvdb_recursively (GvdbTable *table, } } -gboolean -meta_session_manager_save_sync (MetaSessionManager *manager, - GError **error) +static void +meta_session_data_free (MetaSessionData *session_data) +{ + g_clear_pointer (&session_data->new_table, g_hash_table_unref); + g_clear_pointer (&session_data->deleted_sessions, g_hash_table_unref); + g_free (session_data); +} + +static gboolean +meta_session_data_save (MetaSessionData *session_data, + GError **error) { - GHashTableIter iter; - GHashTable *table; - MetaSessionState *session_state; g_autofree char *session_dir = NULL, *session_file = NULL; - if (!manager->name) + if (!session_data->name) { g_set_error (error, G_IO_ERROR, @@ -369,8 +387,7 @@ meta_session_manager_save_sync (MetaSessionManager *manager, } session_dir = g_build_filename (g_get_user_data_dir (), - manager->name, - NULL); + session_data->name, NULL); if (g_mkdir_with_parents (session_dir, 0700) < 0) { @@ -381,39 +398,132 @@ meta_session_manager_save_sync (MetaSessionManager *manager, return FALSE; } - session_file = g_build_filename (session_dir, SESSION_FILE_NAME, NULL); + session_file = g_build_filename (session_dir, + SESSION_FILE_NAME, + NULL); + + if (session_data->gvdb_table) + { + g_auto (GStrv) names; + gsize len, i; + + names = gvdb_table_get_names (session_data->gvdb_table, &len); + + for (i = 0; i < len; i++) + { + if (g_hash_table_contains (session_data->new_table, names[i])) + continue; + if (g_hash_table_contains (session_data->deleted_sessions, names[i])) + continue; + + snapshot_gvdb_recursively (session_data->gvdb_table, + session_data->new_table, names[i]); + } + } + + return gvdb_table_write_contents (session_data->new_table, session_file, + FALSE, error); +} + +static void +save_session_async (GTask *task, + gpointer source_object, + gpointer task_data, + GCancellable *cancellable) +{ + MetaSessionManager *session_manager = source_object; + g_autoptr (GError) error = NULL; + + g_mutex_lock (&session_manager->mutex); + + if (meta_session_data_save (task_data, &error)) + g_task_return_boolean (task, TRUE); + else + g_task_return_error (task, g_steal_pointer (&error)); + + g_mutex_unlock (&session_manager->mutex); +} + +static MetaSessionData * +meta_session_manager_snapshot (MetaSessionManager *manager) +{ + MetaSessionState *session_state; + MetaSessionData *session_data; + GHashTableIter iter; + const gchar *name; + + session_data = g_new0 (MetaSessionData, 1); + session_data->gvdb_table = manager->gvdb_table; + session_data->new_table = gvdb_hash_table_new (NULL, NULL); + session_data->name = manager->name; + + session_data->deleted_sessions = + g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + g_hash_table_iter_init (&iter, manager->deleted_sessions); + while (g_hash_table_iter_next (&iter, (gpointer *) &name, NULL)) + g_hash_table_add (session_data->deleted_sessions, g_strdup (name)); g_hash_table_iter_init (&iter, manager->sessions); - table = gvdb_hash_table_new (NULL, NULL); - while (g_hash_table_iter_next (&iter, NULL, (gpointer*) &session_state)) { GHashTable *session_table; session_table = - gvdb_hash_table_new (table, meta_session_state_get_name (session_state)); + gvdb_hash_table_new (session_data->new_table, + meta_session_state_get_name (session_state)); meta_session_state_serialize (session_state, session_table); } - if (manager->gvdb_table) + return session_data; +} + +void +meta_session_manager_save (MetaSessionManager *manager, + GAsyncReadyCallback cb, + gpointer user_data) +{ + MetaSessionData *session_data; + g_autoptr (GTask) task = NULL; + + task = g_task_new (manager, NULL, cb, user_data); + session_data = meta_session_manager_snapshot (manager); + if (session_data) { - g_auto (GStrv) names; - gsize len, i; + g_task_set_task_data (task, session_data, + (GDestroyNotify) meta_session_data_free); + g_task_run_in_thread (task, save_session_async); + } + else + { + g_task_return_boolean (task, TRUE); + } +} - names = gvdb_table_get_names (manager->gvdb_table, &len); +gboolean +meta_session_manager_save_finish (MetaSessionManager *manager, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (res), error); +} - for (i = 0; i < len; i++) - { - if (g_hash_table_contains (manager->sessions, names[i])) - continue; - if (g_hash_table_contains (manager->deleted_sessions, names[i])) - continue; +gboolean +meta_session_manager_save_sync (MetaSessionManager *manager, + GError **error) +{ + MetaSessionData *session_data; + gboolean retval = TRUE; - snapshot_gvdb_recursively (manager->gvdb_table, table, names[i]); - } + g_mutex_lock (&manager->mutex); + + session_data = meta_session_manager_snapshot (manager); + if (session_data) + { + retval = meta_session_data_save (session_data, error); + meta_session_data_free (session_data); } - return gvdb_table_write_contents (table, - session_file, - FALSE, error); + g_mutex_unlock (&manager->mutex); + + return retval; } diff --git a/src/core/meta-session-manager.h b/src/core/meta-session-manager.h index 16c31bf0b..ba1f08c5f 100644 --- a/src/core/meta-session-manager.h +++ b/src/core/meta-session-manager.h @@ -48,6 +48,14 @@ MetaSessionState * meta_session_manager_get_session (MetaSessionManager *manage GType type, const char *name); +void meta_session_manager_save (MetaSessionManager *manager, + GAsyncReadyCallback cb, + gpointer user_data); + +gboolean meta_session_manager_save_finish (MetaSessionManager *manager, + GAsyncResult *res, + GError **error); + gboolean meta_session_manager_save_sync (MetaSessionManager *manager, GError **error);