summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrick Ohly <patrick.ohly@intel.com>2013-02-21 19:31:38 +0100
committerPatrick Ohly <patrick.ohly@intel.com>2013-02-21 19:31:38 +0100
commit325430b2ca9cb52df907a1402710662bc84b1880 (patch)
treedc5058d2b3ddcca06cc72759f86704c63b722aa7
parent75ff3f87dc89ed760382267d76823aaaebf7a718 (diff)
eds: avoid blocking event processingpohly-event-processing
When there are many incoming D-Bus change notifications, processing of other D-Bus messages can be delayed considerably. This commit is a first step towards solving this by caching the change notifications and processing them with lower priority in a glib idle callback. Ordering of the changes relative to each other is preserved, so semantically this is the same as immediate processing.
-rw-r--r--backends/eds/lib/edsf-persona-store.vala114
1 files changed, 114 insertions, 0 deletions
diff --git a/backends/eds/lib/edsf-persona-store.vala b/backends/eds/lib/edsf-persona-store.vala
index 3d6d37bc..fd8f414a 100644
--- a/backends/eds/lib/edsf-persona-store.vala
+++ b/backends/eds/lib/edsf-persona-store.vala
@@ -2281,8 +2281,105 @@ public class Edsf.PersonaStore : Folks.PersonaStore
}
}
+ // The EDS store receives change notifications as quickly as it
+ // can and then stores them for later processing in a glib idle
+ // callback.
+ //
+ // This avoids problems where many incoming D-Bus change notifications
+ // block processing of other incoming D-Bus messages. Delays of over a minute
+ // have been observed in worst-case (but not entirely unrealistic) scenarios
+ // (1000 contacts added one-by-one while folks is active).
+ //
+ // Cannot store a GLib.SourceFunc directly
+ // in a LinkedList ("Delegates with target are not supported as generic type arguments",
+ // https://mail.gnome.org/archives/vala-list/2011-June/msg00002.html)
+ // and thus have to wrap it in a class.
+ class IdleTask
+ {
+ public GLib.SourceFunc callback;
+ }
+
+
+ private Gee.LinkedList<IdleTask> _idle_tasks = new Gee.LinkedList<IdleTask> ();
+ private uint _idle_handle = 0;
+
+ // Deal with some chunk of work encapsulated in the delegate later.
+ // As in any other SourceFunc, the callback may request to be called
+ // again by returning true. In contrast to Idle.add, _idle_queue
+ // ensures that only one task is processed per idle cycle.
+ private void _idle_queue (owned GLib.SourceFunc callback)
+ {
+ IdleTask task = new IdleTask ();
+ task.callback = (owned) callback;
+ this._idle_tasks.add (task);
+ // Ensure that there is an active idle callback to process
+ // the task, otherwise register it. We cannot just
+ // queue each task separately, because then we might
+ // end up with multiple tasks being done at once
+ // when the process gets idle, instead of one
+ // task at a time.
+ if (this._idle_handle == 0)
+ {
+ this._idle_handle = Idle.add (this._idle_process);
+ }
+ }
+ private bool _idle_process ()
+ {
+ IdleTask? task = this._idle_tasks.peek ();
+ if (task != null)
+ {
+ if (task.callback ())
+ {
+ // Task is not done yet, run it again later.
+ return true;
+ }
+ this._idle_tasks.poll ();
+ }
+
+ // Check for future work.
+ if (this._idle_tasks.is_empty)
+ {
+ // Remember that we need to re-register idle
+ // processing when _idle_queue is called again.
+ this._idle_handle = 0;
+ // Done, will remove idle handler.
+ return false;
+ }
+ else
+ {
+ // Continue processing.
+ return true;
+ }
+ }
+
+ private Gee.LinkedList<E.Contact> _copy_contacts (GLib.List<E.Contact> contacts)
+ {
+ var copy = new Gee.LinkedList<E.Contact> ();
+ foreach (E.Contact c in contacts)
+ {
+ copy.add (c);
+ }
+ return copy;
+ }
+
+ private Gee.LinkedList<string> _copy_contacts_ids (GLib.List<string> contacts_ids)
+ {
+ var copy = new Gee.LinkedList<string> ();
+ foreach (unowned string s in contacts_ids)
+ {
+ copy.add (s);
+ }
+ return copy;
+ }
+
private void _contacts_added_cb (GLib.List<E.Contact> contacts)
{
+ var copy = _copy_contacts (contacts);
+ this._idle_queue (() => { return this._contacts_added_idle (copy); });
+ }
+
+ private bool _contacts_added_idle (Gee.List<E.Contact> contacts)
+ {
HashSet<Persona> added_personas;
/* If the persona store hasn't yet reached quiescence, queue up the
@@ -2318,10 +2415,19 @@ public class Edsf.PersonaStore : Folks.PersonaStore
{
this._emit_personas_changed (added_personas, null);
}
+
+ // Done.
+ return false;
}
private void _contacts_changed_cb (GLib.List<E.Contact> contacts)
{
+ var copy = _copy_contacts (contacts);
+ this._idle_queue (() => { return this._contacts_changed_idle (copy); });
+ }
+
+ private bool _contacts_changed_idle (Gee.List<E.Contact> contacts)
+ {
foreach (E.Contact c in contacts)
{
var iid = Edsf.Persona.build_iid_from_contact (this.id, c);
@@ -2331,10 +2437,17 @@ public class Edsf.PersonaStore : Folks.PersonaStore
((!) persona)._update (c);
}
}
+ return false;
}
private void _contacts_removed_cb (GLib.List<string> contacts_ids)
{
+ var copy = _copy_contacts_ids (contacts_ids);
+ this._idle_queue (() => { return this._contacts_removed_idle (copy); });
+ }
+
+ private bool _contacts_removed_idle (Gee.List<string> contacts_ids)
+ {
var removed_personas = new HashSet<Persona> ();
foreach (string contact_id in contacts_ids)
@@ -2359,6 +2472,7 @@ public class Edsf.PersonaStore : Folks.PersonaStore
{
this._emit_personas_changed (null, removed_personas);
}
+ return false;
}
private void _contacts_complete_cb (Error err)