[evolution] Save state key file asynchronously.
- From: Matthew Barnes <mbarnes src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution] Save state key file asynchronously.
- Date: Wed, 3 Mar 2010 16:22:01 +0000 (UTC)
commit 2026a45165c5c88b5f1c7208f282801b28e81c44
Author: Matthew Barnes <mbarnes redhat com>
Date: Tue Mar 2 23:35:14 2010 -0500
Save state key file asynchronously.
Avoids blocking the UI in case the save triggers an fsync() which, on
ext3, synchronizes the entire filesystem. The logic is a bit tricky
because we want to limit the frequency of saves and only run one save
operation at a time, but we also want to do an immediate last-ditch
save before finalizing the shell view.
shell/e-shell-view.c | 111 ++++++++++++++++++++++++++++++++++++++++---------
1 files changed, 90 insertions(+), 21 deletions(-)
---
diff --git a/shell/e-shell-view.c b/shell/e-shell-view.c
index 83be139..e955518 100644
--- a/shell/e-shell-view.c
+++ b/shell/e-shell-view.c
@@ -25,6 +25,7 @@
#include <glib/gi18n.h>
#include "e-util/e-util.h"
+#include "e-util/e-file-utils.h"
#include "e-util/e-plugin-ui.h"
#include "filter/e-rule-context.h"
@@ -41,7 +42,8 @@ struct _EShellViewPrivate {
gpointer shell_window; /* weak pointer */
GKeyFile *state_key_file;
- guint state_save_source_id;
+ gpointer state_save_activity; /* weak pointer */
+ guint state_save_timeout_id;
gchar *title;
gchar *view_id;
@@ -215,39 +217,98 @@ exit:
g_free (filename);
}
+typedef struct {
+ EShellView *shell_view;
+ gchar *contents;
+} SaveStateData;
+
static void
+shell_view_save_state_done_cb (GFile *file,
+ GAsyncResult *result,
+ SaveStateData *data)
+{
+ GError *error = NULL;
+
+ e_file_replace_contents_finish (file, result, NULL, &error);
+
+ if (error != NULL) {
+ g_warning ("%s", error->message);
+ g_error_free (error);
+ }
+
+ g_object_unref (data->shell_view);
+ g_free (data->contents);
+ g_slice_free (SaveStateData, data);
+}
+
+static EActivity *
shell_view_save_state (EShellView *shell_view)
{
EShellBackend *shell_backend;
+ SaveStateData *data;
+ EActivity *activity;
GKeyFile *key_file;
+ GFile *file;
const gchar *config_dir;
gchar *contents;
- gchar *filename;
- GError *error = NULL;
+ gchar *path;
shell_backend = e_shell_view_get_shell_backend (shell_view);
config_dir = e_shell_backend_get_config_dir (shell_backend);
- filename = g_build_filename (config_dir, "state", NULL);
-
- /* XXX Should do this asynchronously. */
key_file = shell_view->priv->state_key_file;
+
contents = g_key_file_to_data (key_file, NULL, NULL);
- g_file_set_contents (filename, contents, -1, &error);
- g_free (contents);
+ g_return_val_if_fail (contents != NULL, NULL);
- if (error != NULL) {
- g_warning ("%s", error->message);
- g_error_free (error);
- }
+ path = g_build_filename (config_dir, "state", NULL);
+ file = g_file_new_for_path (path);
+ g_free (path);
- g_free (filename);
+ /* GIO does not copy the contents string, so we need to keep
+ * it in memory until saving is complete. We reference the
+ * shell view to keep it from being finalized while saving. */
+ data = g_slice_new (SaveStateData);
+ data->shell_view = g_object_ref (shell_view);
+ data->contents = contents;
+
+ /* The returned activity is a borrowed reference. */
+ activity = e_file_replace_contents_async (
+ file, contents, strlen (contents), NULL,
+ FALSE, G_FILE_CREATE_PRIVATE, (GAsyncReadyCallback)
+ shell_view_save_state_done_cb, data);
+
+#if 0 /* FIXME Enable this for 2.31 */
+ e_activity_set_primary_text (
+ activity, _("Saving user interface state"));
+#endif
+
+ e_shell_backend_add_activity (shell_backend, activity);
+
+ g_object_unref (file);
+
+ return activity;
}
static gboolean
shell_view_state_timeout_cb (EShellView *shell_view)
{
- shell_view_save_state (shell_view);
- shell_view->priv->state_save_source_id = 0;
+ EActivity *activity;
+
+ /* If a save is still in progress, check back later. */
+ if (shell_view->priv->state_save_activity != NULL)
+ return TRUE;
+
+ activity = shell_view_save_state (shell_view);
+
+ /* Set up a weak pointer that gets set to NULL when the
+ * activity finishes. This will tell us if we're still
+ * busy saving state data to disk on the next timeout. */
+ shell_view->priv->state_save_activity = activity;
+ g_object_add_weak_pointer (
+ G_OBJECT (shell_view->priv->state_save_activity),
+ &shell_view->priv->state_save_activity);
+
+ shell_view->priv->state_save_timeout_id = 0;
return FALSE;
}
@@ -421,10 +482,18 @@ shell_view_dispose (GObject *object)
priv = E_SHELL_VIEW_GET_PRIVATE (object);
/* Expedite any pending state saves. */
- if (priv->state_save_source_id > 0) {
- g_source_remove (priv->state_save_source_id);
- priv->state_save_source_id = 0;
- shell_view_save_state (E_SHELL_VIEW (object));
+ if (priv->state_save_timeout_id > 0) {
+ g_source_remove (priv->state_save_timeout_id);
+ priv->state_save_timeout_id = 0;
+ if (priv->state_save_activity == NULL)
+ shell_view_save_state (E_SHELL_VIEW (object));
+ }
+
+ if (priv->state_save_activity != NULL) {
+ g_object_remove_weak_pointer (
+ G_OBJECT (priv->state_save_activity),
+ &priv->state_save_activity);
+ priv->state_save_activity = NULL;
}
if (priv->shell_window != NULL) {
@@ -1331,14 +1400,14 @@ e_shell_view_set_state_dirty (EShellView *shell_view)
g_return_if_fail (E_IS_SHELL_VIEW (shell_view));
/* If a timeout is already scheduled, do nothing. */
- if (shell_view->priv->state_save_source_id > 0)
+ if (shell_view->priv->state_save_timeout_id > 0)
return;
source_id = g_timeout_add_seconds (
STATE_SAVE_TIMEOUT_SECONDS, (GSourceFunc)
shell_view_state_timeout_cb, shell_view);
- shell_view->priv->state_save_source_id = source_id;
+ shell_view->priv->state_save_timeout_id = source_id;
}
/**
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]