diff options
author | Emmanuele Bassi <ebassi@linux.intel.com> | 2010-10-22 16:12:16 +0100 |
---|---|---|
committer | Emmanuele Bassi <ebassi@linux.intel.com> | 2010-10-22 18:08:48 +0100 |
commit | ce5a29bc384542839a5f12061499c8ec706b1c34 (patch) | |
tree | 68f193cd5169f1e07a02a15c1ff9af4d027fbf71 /gtk/gtkrecentmanager.c | |
parent | 5ef2b46d64ebf0a460e92f60e7386a46c3540c9d (diff) | |
download | gtk+-ce5a29bc384542839a5f12061499c8ec706b1c34.tar.gz |
recent-manager: Coalesce multiple changes
Since the ::changed implementation of GtkRecentManager implies a
synchronous write operation, when we receive multiple requests to emit a
::changed signal we might end up blocking.
This change coalesces multiple ::changed emission requests using the
following sequence:
• the first request will install a timeout in 250 ms, which will
emit the ::changed signal
• each further request while the timeout has not been emitted
will increase a counter
‣ if the counter reaches 250 before the timeout has been
emitted, then the RecentManager will remove the timeout
source and force a signal emission and reset the counter
This sequence should guarantee that frequent ::changed emission requests
are coalesced, and also guarantee that we don't let them dangle for too
long.
https://bugzilla.gnome.org/show_bug.cgi?id=616997
Diffstat (limited to 'gtk/gtkrecentmanager.c')
-rw-r--r-- | gtk/gtkrecentmanager.c | 99 |
1 files changed, 71 insertions, 28 deletions
diff --git a/gtk/gtkrecentmanager.c b/gtk/gtkrecentmanager.c index 9730172ba9..ccdea45310 100644 --- a/gtk/gtkrecentmanager.c +++ b/gtk/gtkrecentmanager.c @@ -176,6 +176,9 @@ struct _GtkRecentManagerPrivate GBookmarkFile *recent_items; GFileMonitor *monitor; + + guint changed_timeout; + guint changed_age; }; enum @@ -390,12 +393,26 @@ gtk_recent_manager_get_property (GObject *object, } static void -gtk_recent_manager_dispose (GObject *object) +gtk_recent_manager_finalize (GObject *object) { GtkRecentManager *manager = GTK_RECENT_MANAGER (object); GtkRecentManagerPrivate *priv = manager->priv; - if (priv->monitor) + g_free (priv->filename); + + if (priv->recent_items != NULL) + g_bookmark_file_free (priv->recent_items); + + G_OBJECT_CLASS (gtk_recent_manager_parent_class)->finalize (object); +} + +static void +gtk_recent_manager_dispose (GObject *gobject) +{ + GtkRecentManager *manager = GTK_RECENT_MANAGER (gobject); + GtkRecentManagerPrivate *priv = manager->priv; + + if (priv->monitor != NULL) { g_signal_handlers_disconnect_by_func (priv->monitor, G_CALLBACK (gtk_recent_manager_monitor_changed), @@ -404,21 +421,21 @@ gtk_recent_manager_dispose (GObject *object) priv->monitor = NULL; } - G_OBJECT_CLASS (gtk_recent_manager_parent_class)->dispose (object); -} - -static void -gtk_recent_manager_finalize (GObject *object) -{ - GtkRecentManager *manager = GTK_RECENT_MANAGER (object); - GtkRecentManagerPrivate *priv = manager->priv; + if (priv->changed_timeout != 0) + { + g_source_remove (priv->changed_timeout); + priv->changed_timeout = 0; + priv->changed_age = 0; + } - g_free (priv->filename); - - if (priv->recent_items) - g_bookmark_file_free (priv->recent_items); + if (priv->is_dirty) + { + g_object_ref (manager); + g_signal_emit (manager, signal_changed, 0); + g_object_unref (manager); + } - G_OBJECT_CLASS (gtk_recent_manager_parent_class)->finalize (object); + G_OBJECT_CLASS (gtk_recent_manager_parent_class)->dispose (gobject); } static void @@ -456,8 +473,6 @@ gtk_recent_manager_real_changed (GtkRecentManager *manager) else if (age == 0) { g_bookmark_file_free (priv->recent_items); - priv->recent_items = NULL; - priv->recent_items = g_bookmark_file_new (); } } @@ -942,7 +957,6 @@ gtk_recent_manager_add_full (GtkRecentManager *manager, * will dump our changes */ priv->is_dirty = TRUE; - gtk_recent_manager_changed (manager); return TRUE; @@ -1003,7 +1017,6 @@ gtk_recent_manager_remove_item (GtkRecentManager *manager, } priv->is_dirty = TRUE; - gtk_recent_manager_changed (manager); return TRUE; @@ -1227,7 +1240,6 @@ gtk_recent_manager_move_item (GtkRecentManager *recent_manager, } priv->is_dirty = TRUE; - gtk_recent_manager_changed (recent_manager); return TRUE; @@ -1282,17 +1294,15 @@ purge_recent_items_list (GtkRecentManager *manager, { GtkRecentManagerPrivate *priv = manager->priv; - if (!priv->recent_items) + if (priv->recent_items == NULL) return; - + g_bookmark_file_free (priv->recent_items); - priv->recent_items = NULL; - priv->recent_items = g_bookmark_file_new (); priv->size = 0; - priv->is_dirty = TRUE; - + /* emit the changed signal, to ensure that the purge is written */ + priv->is_dirty = TRUE; gtk_recent_manager_changed (manager); } @@ -1332,10 +1342,43 @@ gtk_recent_manager_purge_items (GtkRecentManager *manager, return purged; } +static gboolean +emit_manager_changed (gpointer data) +{ + GtkRecentManager *manager = data; + + manager->priv->changed_age = 0; + manager->priv->changed_timeout = 0; + + g_signal_emit (manager, signal_changed, 0); + + return FALSE; +} + static void -gtk_recent_manager_changed (GtkRecentManager *recent_manager) +gtk_recent_manager_changed (GtkRecentManager *manager) { - g_signal_emit (recent_manager, signal_changed, 0); + /* coalesce consecutive changes + * + * we schedule a write in 250 msecs immediately; if we get more than one + * request per millisecond before the timeout has a chance to run, we + * schedule an emission immediately. + */ + if (manager->priv->changed_timeout == 0) + manager->priv->changed_timeout = gdk_threads_add_timeout (250, emit_manager_changed, manager); + else + { + manager->priv->changed_age += 1; + + if (manager->priv->changed_age > 250) + { + g_source_remove (manager->priv->changed_timeout); + g_signal_emit (manager, signal_changed, 0); + + manager->priv->changed_age = 0; + manager->priv->changed_timeout = 0; + } + } } static void |