[evolution-data-server/openismus-work-master: 3/10] EBookBackendFile: Avoid lost data in contact modifications



commit f998860768b93cd179ee4471d89fe33d3ba8a858
Author: Tristan Van Berkom <tristanvb openismus com>
Date:   Thu Jan 24 14:57:03 2013 +0900

    EBookBackendFile: Avoid lost data in contact modifications
    
    When modifying contacts, ensure that the contact revisions are in
    sync with the existing ones, otherwise report the E_DATA_BOOK_STATUS_OUT_OF_SYNC
    error. This indicates that the client should refresh it's local copy
    of the contact before trying to modify it again.

 addressbook/backends/file/e-book-backend-file.c |   85 ++++++++++++++++++-----
 1 files changed, 67 insertions(+), 18 deletions(-)
---
diff --git a/addressbook/backends/file/e-book-backend-file.c b/addressbook/backends/file/e-book-backend-file.c
index b86384c..5becc29 100644
--- a/addressbook/backends/file/e-book-backend-file.c
+++ b/addressbook/backends/file/e-book-backend-file.c
@@ -70,6 +70,7 @@ struct _EBookBackendFilePrivate {
 	gchar     *revision;
 	gint       rev_counter;
 	GRecMutex  revision_mutex;
+	GRWLock    lock;
 
 	EBookBackendSqliteDB *sqlitedb;
 };
@@ -613,7 +614,7 @@ e_book_backend_file_new_revision (EBookBackendFile *bf)
  * every DB modification is going to really be an overhead.
  */
 static void
-e_book_backend_file_bump_revision (EBookBackendFile *bf)
+e_book_backend_file_bump_revision_locked (EBookBackendFile *bf)
 {
 	GError *error = NULL;
 
@@ -640,6 +641,14 @@ e_book_backend_file_bump_revision (EBookBackendFile *bf)
 }
 
 static void
+e_book_backend_file_bump_revision (EBookBackendFile *bf)
+{
+	g_rw_lock_writer_lock (&(bf->priv->lock));
+	e_book_backend_file_bump_revision_locked (bf);
+	g_rw_lock_writer_unlock (&(bf->priv->lock));
+}
+
+static void
 e_book_backend_file_load_revision (EBookBackendFile *bf)
 {
 	GError *error = NULL;
@@ -662,18 +671,14 @@ e_book_backend_file_load_revision (EBookBackendFile *bf)
 }
 
 static void
-set_revision (EContact *contact)
+set_revision (EBookBackendFile *bf,
+	      EContact *contact)
 {
-	gchar time_string[100] = {0};
-	const struct tm *tm = NULL;
-	time_t t;
-
-	t = time (NULL);
-	tm = gmtime (&t);
-	if (tm)
-		strftime (time_string, 100, "%Y-%m-%dT%H:%M:%SZ", tm);
-	e_contact_set (contact, E_CONTACT_REV, time_string);
+	gchar *rev;
 
+	rev = e_book_backend_file_new_revision (bf);
+	e_contact_set (contact, E_CONTACT_REV, rev);
+	g_free (rev);
 }
 
 /****************************************************************
@@ -723,7 +728,7 @@ do_create (EBookBackendFile *bf,
 
 		rev = e_contact_get_const (contact, E_CONTACT_REV);
 		if (!(rev && *rev))
-			set_revision (contact);
+			set_revision (bf, contact);
 
 		status = maybe_transform_vcard_for_photo (bf, NULL, contact, perror);
 
@@ -790,9 +795,13 @@ e_book_backend_file_create_contacts (EBookBackendSync *backend,
 {
 	EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend);
 
+	g_rw_lock_writer_lock (&(bf->priv->lock));
+
 	if (do_create (bf, vcards, added_contacts, perror)) {
-		e_book_backend_file_bump_revision (bf);
+		e_book_backend_file_bump_revision_locked (bf);
 	}
+
+	g_rw_lock_writer_unlock (&(bf->priv->lock));
 }
 
 static void
@@ -809,6 +818,8 @@ e_book_backend_file_remove_contacts (EBookBackendSync *backend,
 	gboolean          delete_failed = FALSE;
 	const GSList     *l;
 
+	g_rw_lock_writer_lock (&(bf->priv->lock));
+
 	for (l = id_list; l != NULL; l = l->next) {
 		const gchar *id;
 		EContact *contact;
@@ -855,13 +866,16 @@ e_book_backend_file_remove_contacts (EBookBackendSync *backend,
 			g_propagate_error (perror, local_error);
 		}
 
+		e_book_backend_file_bump_revision_locked (bf);
+
 		*ids = removed_ids;
 	} else {
 		*ids = NULL;
 		e_util_free_string_slist (removed_ids);
 	}
 
-	e_book_backend_file_bump_revision (bf);
+	g_rw_lock_writer_unlock (&(bf->priv->lock));
+
 	e_util_free_object_slist (removed_contacts);
 }
 
@@ -885,9 +899,12 @@ e_book_backend_file_modify_contacts (EBookBackendSync *backend,
 		return;
 	}
 
+	g_rw_lock_writer_lock (&(bf->priv->lock));
+
 	for (l = vcards; l != NULL; l = l->next) {
 		gchar *id;
 		EContact *contact, *old_contact;
+		const gchar *contact_rev, *old_contact_rev;
 
 		contact = e_contact_new_from_vcard (l->data);
 		id = e_contact_get (contact, E_CONTACT_UID);
@@ -914,7 +931,24 @@ e_book_backend_file_modify_contacts (EBookBackendSync *backend,
 			g_object_unref (contact);
 			break;
 		}
-		old_contacts = g_slist_prepend (old_contacts, old_contact);
+
+		contact_rev = e_contact_get_const (contact, E_CONTACT_REV);
+		old_contact_rev = e_contact_get_const (old_contact, E_CONTACT_REV);
+
+		if (!contact_rev || !old_contact_rev ||
+		    strcmp (contact_rev, old_contact_rev) != 0) {
+			g_set_error (perror, E_DATA_BOOK_ERROR,
+				     E_DATA_BOOK_STATUS_OUT_OF_SYNC,
+				     _("Tried to modify contact '%s' with out of sync revision"),
+				     (gchar *)e_contact_get_const (contact, E_CONTACT_UID));
+
+			status = STATUS_ERROR;
+
+			g_free (id);
+			g_object_unref (contact);
+			g_object_unref (old_contact);
+			break;
+		}
 
 		/* Transform incomming photo blobs to uris before storing this to the DB */
 		status = maybe_transform_vcard_for_photo (bf, old_contact, contact, &local_error);
@@ -929,8 +963,9 @@ e_book_backend_file_modify_contacts (EBookBackendSync *backend,
 		}
 
 		/* update the revision (modified time of contact) */
-		set_revision (contact);
+		set_revision (bf, contact);
 
+		old_contacts      = g_slist_prepend (old_contacts, old_contact);
 		modified_contacts = g_slist_prepend (modified_contacts, contact);
 		ids               = g_slist_prepend (ids, id);
 	}
@@ -959,6 +994,11 @@ e_book_backend_file_modify_contacts (EBookBackendSync *backend,
 		}
 	}
 
+	if (status != STATUS_ERROR)
+		e_book_backend_file_bump_revision_locked (bf);
+
+	g_rw_lock_writer_unlock (&(bf->priv->lock));
+
 	if (status != STATUS_ERROR) {
 		*contacts = g_slist_reverse (modified_contacts);
 	} else {
@@ -968,8 +1008,6 @@ e_book_backend_file_modify_contacts (EBookBackendSync *backend,
 
 	e_util_free_string_slist (ids);
 	g_slist_free_full (old_contacts, g_object_unref);
-
-	e_book_backend_file_bump_revision (bf);
 }
 
 static void
@@ -988,9 +1026,12 @@ e_book_backend_file_get_contact (EBookBackendSync *backend,
 		return;
 	}
 
+	g_rw_lock_reader_lock (&(bf->priv->lock));
+
 	*vcard = e_book_backend_sqlitedb_get_vcard_string (bf->priv->sqlitedb,
 							   SQLITEDB_FOLDER_ID, id,
 							   NULL, NULL, &local_error);
+	g_rw_lock_reader_unlock (&(bf->priv->lock));
 
 	if (local_error) {
 
@@ -1027,10 +1068,12 @@ e_book_backend_file_get_contact_list (EBookBackendSync *backend,
 		return;
 	}
 
+	g_rw_lock_reader_lock (&(bf->priv->lock));
 	summary_list = e_book_backend_sqlitedb_search (
 		bf->priv->sqlitedb, SQLITEDB_FOLDER_ID,
 		query, NULL,
 		NULL, NULL, &local_error);
+	g_rw_lock_reader_unlock (&(bf->priv->lock));
 
 	if (summary_list) {
 
@@ -1072,10 +1115,12 @@ e_book_backend_file_get_contact_list_uids (EBookBackendSync *backend,
 		return;
 	}
 
+	g_rw_lock_reader_lock (&(bf->priv->lock));
 	uids = e_book_backend_sqlitedb_search_uids (
 		bf->priv->sqlitedb,
 		SQLITEDB_FOLDER_ID,
 		query, NULL, &local_error);
+	g_rw_lock_reader_unlock (&(bf->priv->lock));
 
 	if (uids == NULL && local_error != NULL) {
 		g_warning ("Failed to fetch contact ids: %s", local_error->message);
@@ -1187,11 +1232,13 @@ book_view_thread (gpointer data)
 	d (printf ("signalling parent thread\n"));
 	e_flag_set (closure->running);
 
+	g_rw_lock_reader_lock (&(bf->priv->lock));
 	summary_list = e_book_backend_sqlitedb_search (
 		bf->priv->sqlitedb,
 		SQLITEDB_FOLDER_ID,
 		query, fields_of_interest,
 		NULL, NULL, &local_error);
+	g_rw_lock_reader_unlock (&(bf->priv->lock));
 
 	if (!summary_list && local_error != NULL) {
 		g_warning (G_STRLOC ": Failed to query initial contacts: %s", local_error->message);
@@ -1606,6 +1653,7 @@ e_book_backend_file_finalize (GObject *object)
 	g_free (priv->photo_dirname);
 	g_free (priv->revision);
 	g_rec_mutex_clear (&priv->revision_mutex);
+	g_rw_lock_clear (&(priv->lock));
 
 	/* Chain up to parent's finalize() method. */
 	G_OBJECT_CLASS (e_book_backend_file_parent_class)->finalize (object);
@@ -1648,6 +1696,7 @@ e_book_backend_file_init (EBookBackendFile *backend)
 	backend->priv = E_BOOK_BACKEND_FILE_GET_PRIVATE (backend);
 
 	g_rec_mutex_init (&backend->priv->revision_mutex);
+	g_rw_lock_init (&(backend->priv->lock));
 
 	g_signal_connect (
 		backend, "notify::online",



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