[evolution-data-server/openismus-work-master: 68/72] EBookBackendFile: Implement cursors.



commit 86d1a12f4fcdbda4d40e74a1e4e6d795f2ee28ab
Author: Tristan Van Berkom <tristanvb openismus com>
Date:   Tue Jul 2 17:45:21 2013 +0900

    EBookBackendFile: Implement cursors.
    
      o Implemented EBookBackend.create_cursor() and EBookBackend.delete_cursor()
        methods using the EDataBookCursorSqlite implementation
    
      o Ensure that all addressbook modifications and revision changes are
        atomic operations, the revision changes happen in the same transactions
        as any other addressbook modification (allowing revision checks to be
        done safely in direct read access mode).

 addressbook/backends/file/e-book-backend-file.c |  286 ++++++++++++++++++++---
 1 files changed, 252 insertions(+), 34 deletions(-)
---
diff --git a/addressbook/backends/file/e-book-backend-file.c b/addressbook/backends/file/e-book-backend-file.c
index c734440..dfb49f4 100644
--- a/addressbook/backends/file/e-book-backend-file.c
+++ b/addressbook/backends/file/e-book-backend-file.c
@@ -79,6 +79,7 @@ struct _EBookBackendFilePrivate {
        gint       rev_counter;
        gboolean   revision_guards;
        GRWLock    lock;
+       GList     *cursors;
 
        EBookBackendSqliteDB *sqlitedb;
 };
@@ -636,27 +637,37 @@ e_book_backend_file_new_revision (EBookBackendFile *bf)
  * its unclear so far if bumping the revision string for 
  * every DB modification is going to really be an overhead.
  */
-static void
-e_book_backend_file_bump_revision (EBookBackendFile *bf)
+static gboolean
+e_book_backend_file_bump_revision (EBookBackendFile *bf,
+                                  GError **error)
 {
-       GError *error = NULL;
+       GError *local_error = NULL;
+       gchar *new_revision;
+       gboolean success;
 
-       g_free (bf->priv->revision);
-       bf->priv->revision = e_book_backend_file_new_revision (bf);
+       new_revision = e_book_backend_file_new_revision (bf);
 
-       if (!e_book_backend_sqlitedb_set_revision (bf->priv->sqlitedb,
-                                                  SQLITEDB_FOLDER_ID,
-                                                  bf->priv->revision,
-                                                  &error)) {
+       success = e_book_backend_sqlitedb_set_revision (bf->priv->sqlitedb,
+                                                       SQLITEDB_FOLDER_ID,
+                                                       bf->priv->revision,
+                                                       &local_error);
+
+       if (success) {
+               g_free (bf->priv->revision);
+               bf->priv->revision = new_revision;
+
+               e_book_backend_notify_property_changed (E_BOOK_BACKEND (bf),
+                                                       BOOK_BACKEND_PROPERTY_REVISION,
+                                                       bf->priv->revision);
+       } else {
+               g_free (new_revision);
                g_warning (
                        G_STRLOC ": Error setting database revision: %s",
-                       error->message);
-               g_error_free (error);
+                       local_error->message);
+               g_propagate_error (error, local_error);
        }
 
-       e_book_backend_notify_property_changed (E_BOOK_BACKEND (bf),
-                                               BOOK_BACKEND_PROPERTY_REVISION,
-                                               bf->priv->revision);
+       return success;
 }
 
 static void
@@ -673,7 +684,7 @@ e_book_backend_file_load_revision (EBookBackendFile *bf)
                        error ? error->message : "Unknown error");
                g_clear_error (&error);
        } else if (bf->priv->revision == NULL) {
-               e_book_backend_file_bump_revision (bf);
+               e_book_backend_file_bump_revision (bf, NULL);
        }
 }
 
@@ -704,6 +715,36 @@ set_revision (EBookBackendFile *bf,
        g_free (rev);
 }
 
+
+/****************************************************************
+ *                   Dealing with cursor updates                *
+ ****************************************************************/
+static void
+cursors_contact_added (EBookBackendFile *bf,
+                      EContact         *contact)
+{
+       GList *l;
+
+       for (l = bf->priv->cursors; l; l = l->next) {
+               EDataBookCursor *cursor = l->data;
+
+               e_data_book_cursor_contact_added (cursor, contact);
+       }
+}
+
+static void
+cursors_contact_removed (EBookBackendFile *bf,
+                        EContact         *contact)
+{
+       GList *l;
+
+       for (l = bf->priv->cursors; l; l = l->next) {
+               EDataBookCursor *cursor = l->data;
+
+               e_data_book_cursor_contact_removed (cursor, contact);
+       }
+}
+
 /****************************************************************
  *                   Main Backend Implementation                *
  ****************************************************************/
@@ -766,7 +807,7 @@ do_create (EBookBackendFile *bf,
 
        if (status != STATUS_ERROR) {
                GList *tail, *link;
-               GSList *slist = NULL;
+               GSList *slist = NULL, *l;
 
                /* XXX EBookBackendSqliteDB still uses GSList. */
                tail = g_queue_peek_tail_link (&queue);
@@ -794,6 +835,11 @@ do_create (EBookBackendFile *bf,
                        status = STATUS_ERROR;
                }
 
+               /* After adding any contacts, notify any cursors that the new contacts are added */
+               for (l = slist; l; l = l->next) {
+                       cursors_contact_added (bf, E_CONTACT (l->data));
+               }
+
                g_slist_free (slist);
        }
 
@@ -993,11 +1039,17 @@ book_backend_file_dispose (GObject *object)
 
        bf = E_BOOK_BACKEND_FILE (object);
 
+       if (bf->priv->cursors) {
+               g_list_free_full (bf->priv->cursors, (GDestroyNotify)g_object_unref);
+               bf->priv->cursors = NULL;
+       }
+
        if (bf->priv->sqlitedb) {
                g_object_unref (bf->priv->sqlitedb);
                bf->priv->sqlitedb = NULL;
        }
 
+
        G_OBJECT_CLASS (e_book_backend_file_parent_class)->dispose (object);
 }
 
@@ -1103,13 +1155,30 @@ book_backend_file_create_contacts_sync (EBookBackend *backend,
                                         GError **error)
 {
        EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend);
-       gboolean success = FALSE;
+       gboolean success;
 
        g_rw_lock_writer_lock (&(bf->priv->lock));
 
-       if (do_create (bf, vcards, out_contacts, error)) {
-               e_book_backend_file_bump_revision (bf);
-               success = TRUE;
+       success = e_book_backend_sqlitedb_lock_updates (bf->priv->sqlitedb, error);
+
+       if (success)
+               success = do_create (bf, vcards, out_contacts, error);
+
+       if (success)
+               success = e_book_backend_file_bump_revision (bf, error);
+
+       if (success)
+               success = e_book_backend_sqlitedb_unlock_updates (bf->priv->sqlitedb,
+                                                                 TRUE, error);
+       else {
+               GError *local_error = NULL;
+
+               /* Rollback transaction */
+               if (!e_book_backend_sqlitedb_unlock_updates (bf->priv->sqlitedb, FALSE, &local_error)) {
+                       g_warning ("Failed to rollback transaction after failing to add contacts: %s",
+                                  local_error->message);
+                       g_clear_error (&local_error);
+               }
        }
 
        g_rw_lock_writer_unlock (&(bf->priv->lock));
@@ -1136,7 +1205,10 @@ book_backend_file_modify_contacts_sync (EBookBackend *backend,
 
        g_rw_lock_writer_lock (&(bf->priv->lock));
 
-       for (ii = 0; ii < length; ii++) {
+       if (!e_book_backend_sqlitedb_lock_updates (bf->priv->sqlitedb, error))
+               status = STATUS_ERROR;
+
+       for (ii = 0; ii < length && status != STATUS_ERROR; ii++) {
                gchar *id;
                EContact *mod_contact, *old_contact;
                const gchar *mod_contact_rev, *old_contact_rev;
@@ -1162,6 +1234,7 @@ book_backend_file_modify_contacts_sync (EBookBackend *backend,
                if (!old_contact) {
                        g_warning (G_STRLOC ": Failed to load contact %s: %s", id, local_error->message);
                        g_propagate_error (error, local_error);
+                       local_error = NULL;
 
                        status = STATUS_ERROR;
 
@@ -1196,6 +1269,7 @@ book_backend_file_modify_contacts_sync (EBookBackend *backend,
                if (status == STATUS_ERROR) {
                        g_warning (G_STRLOC ": Error transforming contact %s: %s", id, local_error->message);
                        g_propagate_error (error, local_error);
+                       local_error = NULL;
 
                        g_free (id);
                        g_object_unref (old_contact);
@@ -1245,6 +1319,7 @@ book_backend_file_modify_contacts_sync (EBookBackend *backend,
                                                           &local_error)) {
                        g_warning ("Failed to modify contacts: %s", local_error->message);
                        g_propagate_error (error, local_error);
+                       local_error = NULL;
 
                        status = STATUS_ERROR;
                }
@@ -1252,14 +1327,49 @@ book_backend_file_modify_contacts_sync (EBookBackend *backend,
                g_slist_free (slist);
        }
 
-       if (status != STATUS_ERROR)
-               e_book_backend_file_bump_revision (bf);
+       /* Bump the revision atomically in the same transaction */
+       if (status != STATUS_ERROR) {
+               if (!e_book_backend_file_bump_revision (bf, error))
+                       status = STATUS_ERROR;
+       }
+
+       /* Commit or rollback transaction */
+       if (status != STATUS_ERROR) {
+
+               if (!e_book_backend_sqlitedb_unlock_updates (bf->priv->sqlitedb,
+                                                            TRUE, error))
+                       status = STATUS_ERROR;
+
+       } else {
+               /* Rollback transaction */
+               if (!e_book_backend_sqlitedb_unlock_updates (bf->priv->sqlitedb, FALSE, &local_error)) {
+                       g_warning ("Failed to rollback transaction after failing to modify contacts: %s",
+                                  local_error->message);
+                       g_clear_error (&local_error);
+               }
+       }
 
        g_rw_lock_writer_unlock (&(bf->priv->lock));
 
        if (status != STATUS_ERROR)
                e_queue_transfer (&mod_contact_queue, out_contacts);
 
+       /* Now that we've modified the contact(s), notify cursors of the changes
+        */
+       if (status != STATUS_ERROR) {
+               GList *l;
+
+               for (l = g_queue_peek_head_link (&old_contact_queue);
+                    l; l = l->next) {
+                       cursors_contact_removed (bf, E_CONTACT (l->data));
+               }
+
+               for (l = g_queue_peek_head_link (&mod_contact_queue);
+                    l; l = l->next) {
+                       cursors_contact_added (bf, E_CONTACT (l->data));
+               }
+       }
+
        while (!g_queue_is_empty (&old_contact_queue))
                g_object_unref (g_queue_pop_head (&old_contact_queue));
 
@@ -1288,7 +1398,9 @@ book_backend_file_remove_contacts_sync (EBookBackend *backend,
 
        g_rw_lock_writer_lock (&(bf->priv->lock));
 
-       for (ii = 0; ii < length; ii++) {
+       success = e_book_backend_sqlitedb_lock_updates (bf->priv->sqlitedb, error);
+
+       for (ii = 0; ii < length && success; ii++) {
                EContact *contact;
 
                /* First load the EContacts which need to be removed, we might delete some
@@ -1317,9 +1429,10 @@ book_backend_file_remove_contacts_sync (EBookBackend *backend,
                                        E_BOOK_CLIENT_ERROR_CONTACT_NOT_FOUND,
                                        _("Contact '%s' not found"), uids[ii]);
                                g_error_free (local_error);
-                       } else
+                       } else {
                                g_propagate_error (error, local_error);
-
+                               local_error = NULL;
+                       }
                        /* Abort as soon as missing contact is to be deleted */
                        success = FALSE;
                        break;
@@ -1341,11 +1454,31 @@ book_backend_file_remove_contacts_sync (EBookBackend *backend,
                        g_propagate_error (error, local_error);
                }
 
-               e_book_backend_file_bump_revision (bf);
+               e_book_backend_file_bump_revision (bf, NULL);
+       }
+
+       /* Commit or rollback transaction */
+       if (success) {
+               success = e_book_backend_sqlitedb_unlock_updates (bf->priv->sqlitedb,
+                                                                 TRUE, error);
+       } else {
+               /* Rollback transaction */
+               if (!e_book_backend_sqlitedb_unlock_updates (bf->priv->sqlitedb, FALSE, &local_error)) {
+                       g_warning ("Failed to rollback transaction after failing to modify contacts: %s",
+                                  local_error->message);
+                       g_clear_error (&local_error);
+               }
        }
 
        g_rw_lock_writer_unlock (&(bf->priv->lock));
 
+       /* After removing any contacts, notify any cursors that the new contacts are added */
+       if (success) {
+               for (l = removed_contacts; l; l = l->next) {
+                       cursors_contact_removed (bf, E_CONTACT (l->data));
+               }
+       }
+
        g_slist_free_full (removed_ids, (GDestroyNotify) g_free);
        g_slist_free_full (removed_contacts, (GDestroyNotify) g_object_unref);
 
@@ -1629,17 +1762,65 @@ book_backend_file_set_locale (EBookBackend *backend,
                              const gchar  *locale)
 {
        EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend);
+       gboolean success;
        GError *error = NULL;
 
-       if (!e_book_backend_sqlitedb_set_locale (bf->priv->sqlitedb,
-                                                SQLITEDB_FOLDER_ID,
-                                                locale,
-                                                &error)) {
+       g_rw_lock_writer_lock (&(bf->priv->lock));
+
+       success = e_book_backend_sqlitedb_lock_updates (bf->priv->sqlitedb,
+                                                       &error);
+
+       if (!success) {
+               g_warning ("Failed to start SQLite transaction: %s", error->message);
+               g_clear_error (&error);
+       }
+
+       if (success) {
+               success = e_book_backend_sqlitedb_set_locale (bf->priv->sqlitedb,
+                                                             SQLITEDB_FOLDER_ID,
+                                                             locale,
+                                                             &error);
+               if (!success) {
+                       g_warning ("Failed to set locale on SQLiteDB: %s", error->message);
+                       g_clear_error (&error);
+               }
+       }
+
+       if (success) {
+               success = e_book_backend_file_bump_revision (bf, &error);
+
+               if (!success) {
+                       g_warning ("Failed to set locale on SQLiteDB: %s", error->message);
+                       g_clear_error (&error);
+               }
+       }
+
+       if (success) {
+               success = e_book_backend_sqlitedb_unlock_updates (bf->priv->sqlitedb,
+                                                                 TRUE, &error);
+
+               if (!success) {
+                       g_warning ("Failed to commit SQLite transaction: %s", error->message);
+                       g_clear_error (&error);
+               }
+
+       } else {
+               GError *error = NULL;
+
+               /* Rollback transaction */
+               if (!e_book_backend_sqlitedb_unlock_updates (bf->priv->sqlitedb, FALSE, &error)) {
+                       g_warning ("Failed to rollback transaction after failing to add contacts: %s",
+                                  error->message);
+                       g_clear_error (&error);
+               }
+       }
+
+       g_rw_lock_writer_unlock (&(bf->priv->lock));
+
+       /* We set the new locale, now update our local variable */
+       if (success) {
                g_free (bf->priv->locale);
                bf->priv->locale = g_strdup (locale);
-
-               g_warning ("Failed to set locale on SQLiteDB: %s", error->message);
-               g_error_free (error);
        }
 }
 
@@ -1651,6 +1832,41 @@ book_backend_file_get_locale (EBookBackend *backend)
        return bf->priv->locale;
 }
 
+static EDataBookCursor *
+book_backend_file_create_cursor (EBookBackend *backend,
+                                EContactField *sort_fields,
+                                EBookSortType *sort_types,
+                                guint n_fields,
+                                GError **error)
+{
+       EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend);
+       EDataBookCursor  *cursor;
+
+       cursor = e_data_book_cursor_sqlite_new (backend,
+                                               bf->priv->sqlitedb,
+                                               SQLITEDB_FOLDER_ID,
+                                               sort_fields,
+                                               sort_types,
+                                               n_fields,
+                                               error);
+
+       if (cursor)
+               bf->priv->cursors =
+                       g_list_prepend (bf->priv->cursors, cursor);
+
+       return cursor;
+}
+
+static void
+book_backend_file_delete_cursor (EBookBackend *backend,
+                                EDataBookCursor *cursor)
+{
+       EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend);
+
+       bf->priv->cursors = g_list_remove (bf->priv->cursors, cursor);
+       g_object_unref (cursor);
+}
+
 static gboolean
 book_backend_file_initable_init (GInitable *initable,
                                  GCancellable *cancellable,
@@ -1817,6 +2033,8 @@ e_book_backend_file_class_init (EBookBackendFileClass *class)
        backend_class->sync = book_backend_file_sync;
        backend_class->set_locale = book_backend_file_set_locale;
        backend_class->get_locale = book_backend_file_get_locale;
+       backend_class->create_cursor = book_backend_file_create_cursor;
+       backend_class->delete_cursor = book_backend_file_delete_cursor;
 }
 
 static void


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