[folks] eds: Handle BUSY errors when opening address books better



commit 9eac06d8f0fe7cb5e58a95d7540f670419d98e54
Author: Philip Withnall <philip tecnocode co uk>
Date:   Fri Jan 6 18:29:21 2012 +0000

    eds: Handle BUSY errors when opening address books better
    
    If another process is in the middle of opening an address book when we come
    to try and open it, our open() call will return immediately with a BUSY
    error.
    
    In this case, we need to wait for the EClient::opened signal to know when
    the open operation has finished.
    
    Hopefully, this will fix those irritating âblah is busyâ errors I frequently
    get when running the EDS backend test suite. Hopefully it also wonât
    introduce too many other problems.

 backends/eds/lib/edsf-persona-store.vala |   86 +++++++++++++++++++++++++++++-
 1 files changed, 85 insertions(+), 1 deletions(-)
---
diff --git a/backends/eds/lib/edsf-persona-store.vala b/backends/eds/lib/edsf-persona-store.vala
index 9351cc4..ccf44eb 100644
--- a/backends/eds/lib/edsf-persona-store.vala
+++ b/backends/eds/lib/edsf-persona-store.vala
@@ -609,7 +609,9 @@ public class Edsf.PersonaStore : Folks.PersonaStore
               ((!) this._addressbook).notify["readonly"].connect (
                   this._address_book_notify_read_only_cb);
 
-              yield ((!) this._addressbook).open (false, null);
+              yield this._open_address_book ();
+              debug ("Successfully finished opening address book %p for " +
+                  "persona store â%sâ (%p).", this._addressbook, this.id, this);
 
               this._update_trust_level ();
             }
@@ -910,6 +912,88 @@ public class Edsf.PersonaStore : Folks.PersonaStore
         }
     }
 
+  /* Temporaries for _open_address_book(). See the complaint below. */
+  Error? _open_address_book_error = null;
+  SourceFunc? _open_address_book_callback = null; /* non-null iff yielded */
+
+  private async void _open_address_book () throws GLib.Error
+    {
+      Error? err_out = null;
+
+      debug ("Opening address book %p for persona store â%sâ (%p)",
+          this._addressbook, this.id, this);
+
+      /* We have to connect to this weirdly because âopenedâ is also a property
+       * name. This means we canât use a lambda function, which in turn means
+       * that we need to build our own closure (or store some temporaries in
+       * the persona storeâs private data struct). Yuck. Yuck. Yuck. */
+      var signal_id = Signal.connect_swapped ((!) this._addressbook, "opened",
+        (Callback) this._address_book_opened_cb, this);
+
+      try
+        {
+          yield ((!) this._addressbook).open (false, null);
+        }
+      catch (GLib.Error e1)
+        {
+          if (e1.domain == Client.error_quark () &&
+              (ClientError) e1.code == ClientError.BUSY)
+            {
+              /* If we've received a BUSY error, it means that the address book
+               * is already in the process of being opened by a different client
+               * (most likely in a completely unrelated process). Since EDS is
+               * kind enough not to block the open() call in this case, we have
+               * to handle it ourselves by waiting for the ::opened signal,
+               * which will be emitted once the address book is opened (or once
+               * opening it fails).
+               *
+               * We yield until the ::opened callback is called, at which point
+               * we return. The callback is a no-op if itâs called during the
+               * open() call above. */
+              this._open_address_book_callback =
+                  this._open_address_book.callback;
+
+              debug ("Yielding on opening address book %p for persona store " +
+                  "â%sâ (%p)", this._addressbook, this.id, this);
+              yield;
+
+              /* Propagate error/success. */
+              err_out = this._open_address_book_error;
+            }
+          else
+            {
+              /* Error. */
+              err_out = e1;
+            }
+
+          if (err_out != null)
+            {
+              throw err_out;
+            }
+        }
+      finally
+        {
+          /* Disconnect the ::opened signal. */
+          ((!) this._addressbook).disconnect (signal_id);
+
+          /* Sanity check. */
+          assert (((!) this._addressbook).is_opened () == true ||
+              err_out != null);
+        }
+    }
+
+  private void _address_book_opened_cb (Error? err, BookClient address_book)
+    {
+      debug ("_address_book_opened_cb for store â%sâ (%p), address book %p " +
+          "and error %p", this.id, this, address_book, (void*) err);
+
+      if (this._open_address_book_callback != null)
+        {
+          this._open_address_book_error = err;
+          this._open_address_book_callback ();
+        }
+    }
+
   private PersonaDetail _eds_field_name_to_folks_persona_detail (
       string eds_field_name)
     {



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