[epiphany/wip/ephy-sync: 41/48] sync-service: Process storage requests in the same order they were sent



commit 4687940e3fabb106f65d2e866f4184402c85f17f
Author: Gabriel Ivascu <ivascu gabriel59 gmail com>
Date:   Mon Aug 1 20:12:13 2016 +0300

    sync-service: Process storage requests in the same order they were sent
    
    Since all storage requests are made asynchronously (thus the exact
    moment when the responses arrive can't be known) and since implementing
    the sync logic requires multiple consecutive storage requests to be
    made, a certain mechanism is needed to ensure that the responses are
    processed in the right order, one after another, to avoid conflicts.
    
    To achieve this, use a queue to hold new requests and a flag to tell
    whether there is currently another request in progress: if there is
    currently another request being transmitted, then the new one has to
    wait in the queue, otherwise, it is free to go. With this, it becomes
    the responsibility of the current request to release the next one
    waiting in the queue once the response has been processed. This is done
    by calling ephy_sync_service_release_next_storage_message() at the end
    of every callback that handles a response from the Storage Server.

 src/ephy-sync-service.c |  116 ++++++++++++++++++++++++++---------------------
 src/ephy-sync-service.h |   62 +++++++++++++------------
 2 files changed, 96 insertions(+), 82 deletions(-)
---
diff --git a/src/ephy-sync-service.c b/src/ephy-sync-service.c
index 657a3ec..34a7f43 100644
--- a/src/ephy-sync-service.c
+++ b/src/ephy-sync-service.c
@@ -50,12 +50,12 @@ struct _EphySyncService {
   gchar *user_email;
   gint64 last_auth_at;
 
+  gboolean is_locked;
   gchar *storage_endpoint;
   gchar *storage_credentials_id;
   gchar *storage_credentials_key;
   gint64 storage_credentials_expiry_time;
-  gboolean is_obtaining_storage_credentials;
-  GQueue *storage_message_queue;
+  GQueue *storage_queue;
 
   gchar *certificate;
   EphySyncCryptoRSAKeyPair *keypair;
@@ -188,6 +188,18 @@ destroy_session_response_cb (SoupSession *session,
   g_object_unref (parser);
 }
 
+static void
+ephy_sync_service_clear_storage_credentials (EphySyncService *self)
+{
+  g_return_if_fail (EPHY_IS_SYNC_SERVICE (self));
+
+  g_clear_pointer (&self->certificate, g_free);
+  g_clear_pointer (&self->storage_endpoint, g_free);
+  g_clear_pointer (&self->storage_credentials_id, g_free);
+  g_clear_pointer (&self->storage_credentials_key, g_free);
+  self->storage_credentials_expiry_time = 0;
+}
+
 static gboolean
 ephy_sync_service_storage_credentials_is_expired (EphySyncService *self)
 {
@@ -349,17 +361,6 @@ ephy_sync_service_send_storage_request (EphySyncService               *self,
   storage_server_request_async_data_free (data);
 }
 
-static void
-ephy_sync_service_send_enqueued_storage_messages (EphySyncService *self)
-{
-  StorageServerRequestAsyncData *qdata;
-
-  while (g_queue_is_empty (self->storage_message_queue) == FALSE) {
-    qdata = g_queue_pop_head (self->storage_message_queue);
-    ephy_sync_service_send_storage_request (self, qdata);
-  }
-}
-
 static gboolean
 ephy_sync_service_certificate_is_valid (EphySyncService *self,
                                         const gchar     *certificate)
@@ -459,22 +460,18 @@ obtain_storage_credentials_response_cb (SoupSession *session,
                json_object_get_string_member (json, "status"),
                json_object_get_string_member (errors, "description"));
     storage_server_request_async_data_free (data);
-    g_object_unref (parser);
+    goto out;
   } else {
     g_warning ("Failed to talk to the Token Server, status code %u. "
                "See https://docs.services.mozilla.com/token/apis.html#error-responses";,
                message->status_code);
     storage_server_request_async_data_free (data);
-    g_object_unref (parser);
+    goto out;
   }
 
-  /* Signal that we are done with obtaining the storage credentials. */
-  service->is_obtaining_storage_credentials = FALSE;
-
-  /* Send the current message and the ones that were waiting in the queue. */
   ephy_sync_service_send_storage_request (service, data);
-  ephy_sync_service_send_enqueued_storage_messages (service);
 
+out:
   g_object_unref (parser);
 }
 
@@ -619,6 +616,30 @@ ephy_sync_service_obtain_signed_certificate (EphySyncService *self,
 }
 
 static void
+ephy_sync_service_issue_storage_request (EphySyncService               *self,
+                                         StorageServerRequestAsyncData *data)
+{
+  g_return_if_fail (EPHY_IS_SYNC_SERVICE (self));
+  g_return_if_fail (data != NULL);
+
+  if (ephy_sync_service_storage_credentials_is_expired (self) == TRUE) {
+    ephy_sync_service_clear_storage_credentials (self);
+
+    /* The only purpose of certificates is to obtain a signed BrowserID that is
+     * needed to talk to the Token Server. From the Token Server we will obtain
+     * the credentials needed to talk to the Storage Server. Since both
+     * ephy_sync_service_obtain_signed_certificate() and
+     * ephy_sync_service_obtain_storage_credentials() complete asynchronously,
+     * we need to entrust them the task of sending the request to the Storage
+     * Server.
+     */
+    ephy_sync_service_obtain_signed_certificate (self, data);
+  } else {
+    ephy_sync_service_send_storage_request (self, data);
+  }
+}
+
+static void
 ephy_sync_service_finalize (GObject *object)
 {
   EphySyncService *self = EPHY_SYNC_SERVICE (object);
@@ -626,7 +647,7 @@ ephy_sync_service_finalize (GObject *object)
   if (self->keypair != NULL)
     ephy_sync_crypto_rsa_key_pair_free (self->keypair);
 
-  g_queue_free_full (self->storage_message_queue, (GDestroyNotify) storage_server_request_async_data_free);
+  g_queue_free_full (self->storage_queue, (GDestroyNotify) storage_server_request_async_data_free);
 
   G_OBJECT_CLASS (ephy_sync_service_parent_class)->finalize (object);
 }
@@ -638,10 +659,7 @@ ephy_sync_service_dispose (GObject *object)
 
   g_clear_object (&self->soup_session);
   g_clear_pointer (&self->user_email, g_free);
-  g_clear_pointer (&self->certificate, g_free);
-  g_clear_pointer (&self->storage_endpoint, g_free);
-  g_clear_pointer (&self->storage_credentials_id, g_free);
-  g_clear_pointer (&self->storage_credentials_key, g_free);
+  ephy_sync_service_clear_storage_credentials (self);
   ephy_sync_service_delete_all_tokens (self);
 
   G_OBJECT_CLASS (ephy_sync_service_parent_class)->dispose (object);
@@ -662,7 +680,7 @@ ephy_sync_service_init (EphySyncService *self)
   gchar *email;
 
   self->soup_session = soup_session_new ();
-  self->storage_message_queue = g_queue_new ();
+  self->storage_queue = g_queue_new ();
 
   email = g_settings_get_string (EPHY_SETTINGS_MAIN, EPHY_PREFS_SYNC_USER);
 
@@ -942,35 +960,29 @@ ephy_sync_service_send_storage_message (EphySyncService     *self,
                                                 modified_since, unmodified_since,
                                                 callback, user_data);
 
-  if (ephy_sync_service_storage_credentials_is_expired (self) == FALSE) {
-    ephy_sync_service_send_storage_request (self, data);
-    return;
-  }
-
-  /* If we are currently obtaining the storage credentials for another message,
-   * then the new message is enqueued and will be sent after the credentials are
-   * retrieved.
+  /* If there is currently another message being transmitted, then the new
+   * message has to wait in the queue, otherwise, it is free to go.
    */
-  if (self->is_obtaining_storage_credentials == TRUE) {
-    g_queue_push_tail (self->storage_message_queue, data);
-    return;
+  if (self->is_locked == FALSE) {
+    self->is_locked = TRUE;
+    ephy_sync_service_issue_storage_request (self, data);
+  } else {
+    g_queue_push_tail (self->storage_queue, data);
   }
+}
 
-  /* This message is the one that will obtain the storage credentials. */
-  self->is_obtaining_storage_credentials = TRUE;
-
-  /* Drop the old certificate and storage credentials. */
-  g_clear_pointer (&self->certificate, g_free);
-  g_clear_pointer (&self->storage_credentials_id, g_free);
-  g_clear_pointer (&self->storage_credentials_key, g_free);
-  self->storage_credentials_expiry_time = 0;
+void
+ephy_sync_service_release_next_storage_message (EphySyncService *self)
+{
+  g_return_if_fail (EPHY_IS_SYNC_SERVICE (self));
+  /* We should never reach this with the service not being locked. */
+  g_assert (self->is_locked == TRUE);
 
-  /* The only purpose of certificates is to obtain a signed BrowserID that is
-   * needed to talk to the Token Server. From the Token Server we will obtain
-   * the credentials needed to talk to the Storage Server. Since both
-   * ephy_sync_service_obtain_signed_certificate() and
-   * ephy_sync_service_obtain_storage_credentials() complete asynchronously, we
-   * need to entrust them the task of sending the request to the Storage Server.
+  /* If there are other messages waiting in the queue, we release the next one
+   * and keep the service locked, else, we mark the service as not locked.
    */
-  ephy_sync_service_obtain_signed_certificate (self, data);
+  if (g_queue_is_empty (self->storage_queue) == FALSE)
+    ephy_sync_service_issue_storage_request (self, g_queue_pop_head (self->storage_queue));
+  else
+    self->is_locked = FALSE;
 }
diff --git a/src/ephy-sync-service.h b/src/ephy-sync-service.h
index 6623bad..8dd043b 100644
--- a/src/ephy-sync-service.h
+++ b/src/ephy-sync-service.h
@@ -37,47 +37,49 @@ typedef enum {
   TOKEN_KB
 } EphySyncServiceTokenType;
 
-EphySyncService *ephy_sync_service_new                  (void);
+EphySyncService *ephy_sync_service_new                          (void);
 
-const gchar     *ephy_sync_service_token_name_from_type (EphySyncServiceTokenType token_type);
+const gchar     *ephy_sync_service_token_name_from_type         (EphySyncServiceTokenType token_type);
 
-gboolean         ephy_sync_service_is_signed_in         (EphySyncService *self);
+gboolean         ephy_sync_service_is_signed_in                 (EphySyncService *self);
 
-gchar           *ephy_sync_service_get_user_email       (EphySyncService *self);
+gchar           *ephy_sync_service_get_user_email               (EphySyncService *self);
 
-void             ephy_sync_service_set_user_email       (EphySyncService *self,
-                                                         const gchar     *email);
+void             ephy_sync_service_set_user_email               (EphySyncService *self,
+                                                                 const gchar     *email);
 
-gchar           *ephy_sync_service_get_token            (EphySyncService          *self,
-                                                         EphySyncServiceTokenType  token_type);
+gchar           *ephy_sync_service_get_token                    (EphySyncService          *self,
+                                                                 EphySyncServiceTokenType  token_type);
 
-void             ephy_sync_service_set_token            (EphySyncService          *self,
-                                                         gchar                    *token_value,
-                                                         EphySyncServiceTokenType  token_type);
+void             ephy_sync_service_set_token                    (EphySyncService          *self,
+                                                                 gchar                    *token_value,
+                                                                 EphySyncServiceTokenType  token_type);
 
-void             ephy_sync_service_set_and_store_tokens (EphySyncService          *self,
-                                                         gchar                    *token_value,
-                                                         EphySyncServiceTokenType  token_type,
-                                                         ...) G_GNUC_NULL_TERMINATED;
+void             ephy_sync_service_set_and_store_tokens         (EphySyncService          *self,
+                                                                 gchar                    *token_value,
+                                                                 EphySyncServiceTokenType  token_type,
+                                                                 ...) G_GNUC_NULL_TERMINATED;
 
-void             ephy_sync_service_delete_all_tokens    (EphySyncService *self);
+void             ephy_sync_service_delete_all_tokens            (EphySyncService *self);
 
-void             ephy_sync_service_destroy_session      (EphySyncService *self,
-                                                         const gchar     *sessionToken);
+void             ephy_sync_service_destroy_session              (EphySyncService *self,
+                                                                 const gchar     *sessionToken);
 
-gboolean         ephy_sync_service_fetch_sync_keys      (EphySyncService *self,
-                                                         const gchar     *email,
-                                                         const gchar     *keyFetchToken,
-                                                         const gchar     *unwrapBKey);
+gboolean         ephy_sync_service_fetch_sync_keys              (EphySyncService *self,
+                                                                 const gchar     *email,
+                                                                 const gchar     *keyFetchToken,
+                                                                 const gchar     *unwrapBKey);
 
-void             ephy_sync_service_send_storage_message (EphySyncService     *self,
-                                                         gchar               *endpoint,
-                                                         const gchar         *method,
-                                                         gchar               *request_body,
-                                                         double               modified_since,
-                                                         double               unmodified_since,
-                                                         SoupSessionCallback  callback,
-                                                         gpointer             user_data);
+void             ephy_sync_service_send_storage_message         (EphySyncService     *self,
+                                                                 gchar               *endpoint,
+                                                                 const gchar         *method,
+                                                                 gchar               *request_body,
+                                                                 double               modified_since,
+                                                                 double               unmodified_since,
+                                                                 SoupSessionCallback  callback,
+                                                                 gpointer             user_data);
+
+void             ephy_sync_service_release_next_storage_message (EphySyncService *self);
 
 G_END_DECLS
 


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