
---
 addressbook/libedata-book/e-data-book.c |  128 ++++++++++++++++++++++++++++++-
 1 files changed, 124 insertions(+), 4 deletions(-)

diff --git a/addressbook/libedata-book/e-data-book.c b/addressbook/libedata-book/e-data-book.c
index a27a671..ecc537d 100644
--- a/addressbook/libedata-book/e-data-book.c
+++ b/addressbook/libedata-book/e-data-book.c
@@ -138,6 +138,20 @@ typedef struct {
 static GHashTable *modules_table = NULL;
 G_LOCK_DEFINE (modules_table);
 
+/* Used for tracking the referencing of books.
+ * When a book is last unreferenced, its
+ * corresponding key in the hash table
+ * is set to 0 so that a new attempt
+ * to access the book is blocked. This is
+ * done because the book may be accessed
+ * from the main thread when locale changed
+ * and the book is in course of being freed
+ * in another thread.
+ *
+ */
+static GHashTable *book_table = NULL;
+G_LOCK_DEFINE (book_table);
+
 /* Forward Declarations */
 static void                 e_data_book_initable_init  (GInitableIface *interface);
 static DirectOperationData *direct_operation_data_push (EDataBook          *book,
@@ -296,6 +310,77 @@ op_ref (OperationData *data)
 	return data;
 }
 
+/* Wrappers that help keep track of each book's
+ * reference count, so that there are no attempts
+ * to access its private structure from another thread
+ * if it has been or is in course of being closed
+ */
+
+static gpointer
+book_ref (gpointer book)
+{
+	gpointer orig_key, value;
+
+	G_LOCK(book_table);
+	if (book_table && g_hash_table_lookup_extended(book_table, book, &orig_key, &value)) {
+		if (orig_key && value) {
+			gint *int_v = (gint*)value;
+			(*int_v)++;
+		}
+	}
+
+	G_UNLOCK(book_table);
+	return g_object_ref(book);
+}
+
+static void
+book_unref (gpointer book)
+{
+	gpointer orig_key, value;
+
+	G_LOCK(book_table);
+	if (book_table && g_hash_table_lookup_extended(book_table, book, &orig_key, &value)) {
+		if (orig_key && value) {
+			gint *int_v = (gint*)value;
+			(*int_v)--;
+		}
+
+	}
+
+	/* Unref is done while locked so that
+	 * if this is called from another
+	 * thread, then the main one won't try
+	 * to access the book's private struct
+	 * while dispose and finalize are being called
+	 */
+
+	g_object_unref (book);
+	G_UNLOCK(book_table);
+}
+
+/* Checks if unref was called on a book
+ * and its reference count dropped to 0.
+ * If so, it returns. The book table is
+ * guarded by a lock where this function
+ * is called to block if another thread
+ * was preparing to call dispose on the book.
+ */
+
+static gboolean
+book_is_available (gpointer book)
+{
+	gpointer orig_key, value;
+	if (book_table && g_hash_table_lookup_extended(book_table, book, &orig_key, &value)) {
+		if (orig_key && value) {
+			gint *int_v = (gint*)value;
+			if (*int_v > 0)
+				return TRUE;
+		}
+	}
+
+	return FALSE;
+}
+
 static OperationData *
 op_new (OperationID op,
         EDataBook *book,
@@ -307,7 +392,7 @@ op_new (OperationID op,
 	data->ref_count = 1;
 	data->op = op;
 	data->id = e_operation_pool_reserve_opid (ops_pool);
-	data->book = g_object_ref (book);
+	data->book = book_ref (book);
 	data->cancellable = g_cancellable_new ();
 
 	/* This is optional so we can fake client requests. */
@@ -371,7 +456,7 @@ op_unref (OperationData *data)
 				break;
 		}
 
-		g_object_unref (data->book);
+		book_unref (data->book);
 		g_object_unref (data->cancellable);
 
 		if (data->invocation != NULL)
@@ -770,7 +855,8 @@ op_direct_new (OperationID op,
 	data = g_slice_new0 (OperationData);
 	data->ref_count = 1;
 	data->op = op;
-	data->book = g_object_ref (book);
+
+	data->book = book_ref (book);
 	data->id = e_operation_pool_reserve_opid (ops_pool);
 
 	if (cancellable)
@@ -1287,7 +1373,7 @@ data_book_handle_close_cb (EDBusAddressBook *interface,
 
 	op = op_new (OP_CLOSE, book, invocation);
 	/* unref here makes sure the book is freed in a separate thread */
-	g_object_unref (book);
+	book_unref (book);
 
 	/* This operation is never queued. */
 	e_operation_pool_push (ops_pool, op);
@@ -2029,6 +2115,17 @@ data_book_locale_changed (GObject *object,
 	EDataBook *book = (EDataBook *)user_data;
 	EBookBackend *backend;
 
+	/* Make sure that the book isn't closed
+	 * or in progress of being freed
+	 *
+	 */
+
+	G_LOCK(book_table);
+	if (!book_is_available(book)) {
+		G_UNLOCK(book_table);
+		return;
+	}
+
 	backend = book->priv->backend;
 
 	if (backend) {
@@ -2047,6 +2144,7 @@ data_book_locale_changed (GObject *object,
 
 		g_free (interpreted_locale);
 	}
+	G_UNLOCK(book_table);
 }
 
 static void
@@ -2057,6 +2155,17 @@ data_book_localed_ready (GObject *source_object,
 	EDataBook *book = (EDataBook *)user_data;
 	GError *error = NULL;
 
+	/* Make sure that the book isn't closed
+	 * or in progress of being freed
+	 *
+	 */
+
+	G_LOCK(book_table);
+	if (!book_is_available(book)) {
+		G_UNLOCK(book_table);
+		return;
+	}
+
 	book->priv->localed_proxy = e_dbus_locale1_proxy_new_finish (res, &error);
 
 	if (book->priv->localed_proxy == NULL) {
@@ -2069,6 +2178,7 @@ data_book_localed_ready (GObject *source_object,
 		book->priv->localed_cancel = NULL;
 	}
 
+	G_UNLOCK(book_table);
 	if (book->priv->localed_proxy) {
 		g_signal_connect (book->priv->localed_proxy, "notify::locale",
 				  G_CALLBACK (data_book_locale_changed), book);
@@ -2498,6 +2608,16 @@ e_data_book_initable_init (GInitableIface *interface)
 static void
 e_data_book_init (EDataBook *ebook)
 {
+	gint *initial_refcount = g_malloc(sizeof(gint));
+
+	G_LOCK(book_table);
+	if (!book_table)
+		book_table = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free);
+
+	*initial_refcount = 1;
+	g_hash_table_insert(book_table, ebook, initial_refcount);
+	G_UNLOCK(book_table);
+
 	ebook->priv = E_DATA_BOOK_GET_PRIVATE (ebook);
 
 	ebook->priv->pending_ops = g_hash_table_new_full (
-- 
1.7.5.4

