[folks] libsocialweb: Tidy up Swf.PersonaStore.prepare() to return asynchronously



commit 699d009e2673bc9d078b43cdd20e5224f868bebd
Author: Philip Withnall <philip tecnocode co uk>
Date:   Sun Mar 4 20:24:49 2012 +0000

    libsocialweb: Tidy up Swf.PersonaStore.prepare() to return asynchronously
    
    Previously, the prepare() method would return synchronously, firing off
    some asynchronous methods in libsocialweb, which would later return in
    callbacks and (theoretically) cause the persona store to reach quiescence.
    
    This behaviour was not playing nicely with the expected asynchronous
    behaviour of prepare(), and wasn't handling errors properly â on an error,
    the code would cancel preparation, but not throw an error or remove the
    persona store.
    
    The code now throws errors and removes the persona store if preparation
    fails, which should hopefully help some of the not-reaching-quiescence issues
    weâve been seeing.
    
    Helps: https://bugzilla.gnome.org/show_bug.cgi?id=670191

 backends/libsocialweb/lib/swf-persona-store.vala |  241 +++++++++++++++-------
 1 files changed, 165 insertions(+), 76 deletions(-)
---
diff --git a/backends/libsocialweb/lib/swf-persona-store.vala b/backends/libsocialweb/lib/swf-persona-store.vala
index b26c131..8a00403 100644
--- a/backends/libsocialweb/lib/swf-persona-store.vala
+++ b/backends/libsocialweb/lib/swf-persona-store.vala
@@ -212,12 +212,96 @@ public class Swf.PersonaStore : Folks.PersonaStore
           "Personas cannot be removed from this store.");
     }
 
+  private async string[]? _get_static_capabilities () throws GLib.Error
+    {
+      /* Take a reference to the PersonaStore while waiting for the async call
+       * to return. See: bgo#665039. */
+      this.ref ();
+
+      var received_callback = false;
+      var has_yielded = false;
+
+      string[]? caps = null;
+      Error? error = null;
+
+      this.service.get_static_capabilities ((service, _caps, _error) =>
+        {
+          received_callback = true;
+
+          caps = _caps;
+          error = _error;
+
+          if (has_yielded == true)
+            {
+              this._get_static_capabilities.callback ();
+            }
+        });
+
+      /* Yield for the get_static_capabilities() callback to be invoked, if it
+       * hasn't already been invoked (which could happen if
+       * get_static_capabilities() called it immediately). */
+      if (received_callback == false)
+        {
+          has_yielded = true;
+          yield;
+        }
+
+      this.unref ();
+
+      /* Handle the error, if it was set. */
+      if (error != null)
+        {
+          throw error;
+        }
+
+      return caps;
+    }
+
+  private async ClientContactView? _contacts_query_open_view (string query,
+      HashTable<weak string, weak string> parameters)
+    {
+      /* Take a reference to the PersonaStore while waiting for the async call
+       * to return. See: bgo#665039. */
+      this.ref ();
+
+      var received_callback = false;
+      var has_yielded = false;
+
+      ClientContactView? contact_view = null;
+
+      this.service.contacts_query_open_view (query, parameters,
+          (service, _contact_view) =>
+        {
+          received_callback = true;
+
+          contact_view = _contact_view;
+
+          if (has_yielded == true)
+            {
+              this._contacts_query_open_view.callback ();
+            }
+        });
+
+      /* Yield for the contacts_query_open_view() callback to be invoked, if it
+       * hasn't already been invoked (which could happen if
+       * contacts_query_open_view() called it immediately). */
+      if (received_callback == false)
+        {
+          has_yielded = true;
+          yield;
+        }
+
+      this.unref ();
+
+      return contact_view;
+    }
+
   /**
    * Prepare the PersonaStore for use.
    *
    * See { link Folks.PersonaStore.prepare}.
    */
-  public override async void prepare ()
+  public override async void prepare () throws GLib.Error
     {
       lock (this._is_prepared)
         {
@@ -225,83 +309,88 @@ public class Swf.PersonaStore : Folks.PersonaStore
             {
               this._prepare_pending = true;
 
-              /* Take a reference to the PersonaStore while waiting for the
-               * async call to return. See: bgo#665039. */
-              this.ref ();
+              /* Get the service's capabilities. */
+              string[]? caps = null;
+
+              try
+                {
+                  caps = yield this._get_static_capabilities ();
 
-              this.service.get_static_capabilities (
-                  (service, caps, error) =>
+                  if (caps == null)
                     {
-                      if (caps == null)
-                        {
-                          this.unref ();
-                          this._prepare_pending = false;
-                          return;
-                        }
-
-                      bool has_contacts = ClientService.has_cap (caps,
-                          "has-contacts-query-iface");
-                      if (!has_contacts)
-                        {
-                          this.unref ();
-                          this._prepare_pending = false;
-                          return;
-                        }
-
-                      var parameters = new HashTable<weak string, weak string>
-                          (str_hash, str_equal);
-
-                      /* Take another ref for this async call. */
-                      this.ref ();
-
-                      this.service.contacts_query_open_view
-                          ("people", parameters, (query, contact_view) =>
-                        {
-                          /* The D-Bus call could return an error. In this
-                           * case, contact_view is null */
-                          if (contact_view == null)
-                            {
-                              this.unref ();
-                              this._prepare_pending = false;
-                              return;
-                            }
-
-                          contact_view.contacts_added.connect
-                              (this.contacts_added_cb);
-                          contact_view.contacts_changed.connect
-                              (this.contacts_changed_cb);
-                          contact_view.contacts_removed.connect
-                              (this.contacts_removed_cb);
-
-                          this._contact_view = contact_view;
-                          this._is_prepared = true;
-                          this._prepare_pending = false;
-                          this.notify_property ("is-prepared");
-
-                          /* FIXME: for lsw Stores with 0 contacts or badly
-                           * configured (or not authenticated, etc) we are
-                           * condemned to never reach quiescence if we wait for
-                           * contacts to be added. A possible way around this
-                           * would be, if libsocialweb provided such properties,
-                           * to query the social client to see if it's available
-                           * (authenticated and ready) and the number of
-                           * contacts that we would (eventually) get. That is
-                           * the only way we could ever reach quiescence without
-                           * waiting for eternity.
-                           *
-                           * See:
-                           * https://bugzilla.gnome.org/show_bug.cgi?id=658445
-                           */
-                          this._is_quiescent = true;
-                          this.notify_property ("is-quiescent");
-
-                          this._contact_view.start ();
-
-                          this.unref ();
-                        });
-
-                      this.unref ();
-                    });
+                      throw new PersonaStoreError.INVALID_ARGUMENT (
+                          /* Translators: the parameter is an error message. */
+                          _("Couldnât prepare libsocialweb service: %s"),
+                          _("No capabilities were found."));
+                    }
+                }
+              catch (GLib.Error e1)
+                {
+                  /* Remove the persona store on error */
+                  this.removed ();
+                  this._prepare_pending = false;
+
+                  throw e1;
+                }
+
+              /* Check for the contacts query interface. */
+              bool has_contacts = ClientService.has_cap (caps,
+                  "has-contacts-query-iface");
+              if (!has_contacts)
+                {
+                  /* Remove the persona store on error */
+                  this.removed ();
+                  this._prepare_pending = false;
+
+                  throw new PersonaStoreError.INVALID_ARGUMENT (
+                      /* Translators: the parameter is an error message. */
+                      _("Couldnât prepare libsocialweb service: %s"),
+                      _("No contacts capability was found."));
+                }
+
+              /* Open a contacts query view. */
+              var contact_view = yield this._contacts_query_open_view ("people",
+                  new HashTable<weak string, weak string> (str_hash,
+                      str_equal));
+
+              /* Propagate errors from the contacts_query_open_view()
+               * callback. */
+              if (contact_view == null)
+                {
+                  /* Remove the persona store on error */
+                  this.removed ();
+                  this._prepare_pending = false;
+
+                  throw new PersonaStoreError.INVALID_ARGUMENT (
+                      /* Translators: the parameter is an error message. */
+                      _("Couldnât prepare libsocialweb service: %s"),
+                      _("Error opening contacts view."));
+                }
+
+              contact_view.contacts_added.connect (this.contacts_added_cb);
+              contact_view.contacts_changed.connect (this.contacts_changed_cb);
+              contact_view.contacts_removed.connect (this.contacts_removed_cb);
+
+              this._contact_view = contact_view;
+              this._is_prepared = true;
+              this._prepare_pending = false;
+              this.notify_property ("is-prepared");
+
+              /* FIXME: for lsw Stores with 0 contacts or badly configured (or
+               * not authenticated, etc) we are condemned to never reach
+               * quiescence if we wait for contacts to be added. A possible way
+               * around this would be, if libsocialweb provided such properties,
+               * to query the social client to see if it's available
+               * (authenticated and ready) and the number of contacts that we
+               * would (eventually) get. That is the only way we could ever
+               * reach quiescence without waiting for eternity.
+               *
+               * See: https://bugzilla.gnome.org/show_bug.cgi?id=658445
+               */
+              this._is_quiescent = true;
+              this.notify_property ("is-quiescent");
+
+              this._contact_view.start ();
             }
         }
     }



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]