Patch: imap idle loops stop_idle_connect_lock while it cannot get the lock



	Hi,

	This patch should fix a deathlock in IMAP IDLE code. This happens
mainly on move to operation.

	Description of the problem:
	* We have two IMAP IDLE accounts, A and B.
	* We move a folder from account A to account B. This operation occurs
in the thread of the camel queue for account A.
	* In the same time, camel queue of account B is doing operations (for
example a poke status).

	Ok, in this case we have the camel queues for accounts A and B trying
to stop the IDLE and get the connection lock. This operation can easily
cause deathlocks.

	To avoid this, we add an active wait (with usleep) for tryint to get
the lock. This avoids this race condition.

Changelog entry:
	* libtinymail-camel/camel-lite/camel/providers/imap/camel-imap-store.c:
	now *_stop_idle_connect_lock loops if it cannot get the connect lock
	to avoid interlocks if two concurrent threads try to get the lock.

-- 
José Dapena Paz <jdapena igalia com>
Igalia
diff --git a/ChangeLog b/ChangeLog
index 8b33b21..f81593b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2009-01-08  Jose Dapena Paz  <jdapena igalia com>
+
+	* libtinymail-camel/camel-lite/camel/providers/imap/camel-imap-store.c:
+	now *_stop_idle_connect_lock loops if it cannot get the connect lock
+	to avoid interlocks if two concurrent threads try to get the lock.
+
 2008-12-15  Jose Dapena Paz  <jdapena igalia com>
 
 	* libtinymail-camel/tny-camel-account-store.c: removed unnecesary camel
diff --git a/libtinymail-camel/camel-lite/camel/providers/imap/camel-imap-store.c b/libtinymail-camel/camel-lite/camel/providers/imap/camel-imap-store.c
index dac9431..ecdac46 100644
--- a/libtinymail-camel/camel-lite/camel/providers/imap/camel-imap-store.c
+++ b/libtinymail-camel/camel-lite/camel/providers/imap/camel-imap-store.c
@@ -83,6 +83,7 @@
 /* Specified in RFC 2060 */
 #define IMAP_PORT "143"
 #define IMAPS_PORT "993"
+#define STOP_IDLE_LOOP_WAIT 100000
 
 #ifdef G_OS_WIN32
 /* The strtok() in Microsoft's C library is MT-safe (but still uses
@@ -325,6 +326,7 @@ _camel_imap_store_stop_idle_connect_lock (CamelImapStore *store)
 #ifdef IDLE_DEBUG
 	gint depth;
 #endif
+	gboolean connection_locked = FALSE;
 	/* This operation should behave as atomic. This should open
 	 * an area where imap operations requested by user are executed,
 	 * and block idle loop meanwhile.
@@ -341,24 +343,33 @@ _camel_imap_store_stop_idle_connect_lock (CamelImapStore *store)
 	}
 
 	idle_debug ("Waiting for idle wait reasons lock\n");
-	g_static_rec_mutex_lock (store->idle_wait_reasons_lock);
-	idle_debug ("Idle wait reasons lock depth: %d\n", store->idle_wait_reasons_lock->depth);
+	while (!connection_locked) {
+		g_static_rec_mutex_lock (store->idle_wait_reasons_lock);
+		idle_debug ("Idle wait reasons lock depth: %d\n", store->idle_wait_reasons_lock->depth);
 
-	store->idle_wait_reasons++;
-	idle_debug ("Idle wait reasons %d -> %d\n", store->idle_wait_reasons - 1, store->idle_wait_reasons);
+		store->idle_wait_reasons++;
+		idle_debug ("Idle wait reasons %d -> %d\n", store->idle_wait_reasons - 1, store->idle_wait_reasons);
 		
-	CAMEL_SERVICE_REC_LOCK (store, connect_lock);
-	if (store->capabilities & IMAP_CAPABILITY_IDLE) {
-		if (!store->already_in_stop_idle) {
-			store->already_in_stop_idle = TRUE;
-			camel_imap_store_stop_idle (store);
-			store->already_in_stop_idle = FALSE;
-		}
-	}
+		if (connection_locked = CAMEL_SERVICE_REC_TRYLOCK (store, connect_lock)) {
+			if (store->capabilities & IMAP_CAPABILITY_IDLE) {
+				if (!store->already_in_stop_idle) {
+					store->already_in_stop_idle = TRUE;
+					camel_imap_store_stop_idle (store);
+					store->already_in_stop_idle = FALSE;
+				}
+			}
 #ifdef IDLE_DEBUG
-	depth = store->idle_wait_reasons_lock->depth;
+			depth = store->idle_wait_reasons_lock->depth;
 #endif
-	g_static_rec_mutex_unlock (store->idle_wait_reasons_lock);
+		}
+		
+		g_static_rec_mutex_unlock (store->idle_wait_reasons_lock);
+		if (!connection_locked) {
+			store->idle_wait_reasons--;
+			idle_debug ("Looping stop_idle_connect_lock\n");
+			usleep (STOP_IDLE_LOOP_WAIT);
+		}
+	}
 	idle_debug ("Idle wait reasons lock depth (%d->%d)\n", depth, depth - 1);
 		
 }


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