[evolution-data-server/openismus-work-master: 73/81] EBookBackendFile: Implement cursors.
- From: Tristan Van Berkom <tvb src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution-data-server/openismus-work-master: 73/81] EBookBackendFile: Implement cursors.
- Date: Tue, 30 Jul 2013 21:46:31 +0000 (UTC)
commit 82f073c6b2bb0521bf0054305ee0ab01109cd110
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 | 304 ++++++++++++++++++++---
1 files changed, 270 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..79c2653 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,
+ new_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,52 @@ set_revision (EBookBackendFile *bf,
g_free (rev);
}
+
+/****************************************************************
+ * Dealing with cursor updates *
+ ****************************************************************/
+static void
+cursors_locale_changed (EBookBackendFile *bf)
+{
+ GList *l;
+
+ for (l = bf->priv->cursors; l; l = l->next) {
+ EDataBookCursor *cursor = l->data;
+ GError *error = NULL;
+
+ if (!e_data_book_cursor_load_locale (cursor, NULL, &error)) {
+ g_warning ("Error loading cursor locale: %s", error->message);
+ g_clear_error (&error);
+ }
+ }
+}
+
+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 +823,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 +851,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 +1055,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 +1171,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 +1221,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 +1250,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 +1285,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 +1335,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 +1343,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 +1414,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 +1445,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 +1470,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 +1778,67 @@ 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));
+
+ cursors_locale_changed (bf);
+
+ /* 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 +1850,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 +2051,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]