Blob Blame History Raw
diff -up evolution-data-server-3.12.4/camel/providers/imapx/camel-imapx-server.c.imapx-job-stuck-with-idle evolution-data-server-3.12.4/camel/providers/imapx/camel-imapx-server.c
--- evolution-data-server-3.12.4/camel/providers/imapx/camel-imapx-server.c.imapx-job-stuck-with-idle	2014-07-13 20:22:01.000000000 +0200
+++ evolution-data-server-3.12.4/camel/providers/imapx/camel-imapx-server.c	2014-07-18 11:59:00.797448928 +0200
@@ -320,6 +320,7 @@ typedef enum {
 	IMAPX_IDLE_STARTED,	/* IDLE continuation received; IDLE active */
 	IMAPX_IDLE_CANCEL,	/* Cancelled from ISSUED state; need to send
 				   DONE as soon as we receive continuation */
+	IMAPX_IDLE_WAIT_DONE	/* DONE was issued, waiting for a confirmation response */
 } CamelIMAPXIdleState;
 
 #define IMAPX_IDLE_DWELL_TIME	2 /* Number of seconds to remain in PENDING
@@ -390,7 +391,7 @@ struct _CamelIMAPXServerPrivate {
 	gchar inbox_separator;
 
 	/* IDLE support */
-	GMutex idle_lock;
+	GRecMutex idle_lock;
 	GThread *idle_thread;
 	GMainLoop *idle_main_loop;
 	GMainContext *idle_main_context;
@@ -1472,7 +1473,7 @@ imapx_command_start_next (CamelIMAPXServ
 						"waiting for idle to stop \n");
 					/* if there are more pending commands,
 					 * then they should be processed too */
-					break;
+					return;
 
 				case IMAPX_IDLE_STOP_ERROR:
 					return;
@@ -3054,25 +3055,27 @@ imapx_continuation (CamelIMAPXServer *is
 			return FALSE;
 
 		c (is->tagprefix, "Got continuation response for IDLE \n");
-		g_mutex_lock (&is->priv->idle_lock);
+		g_rec_mutex_lock (&is->priv->idle_lock);
 		/* We might have actually sent the DONE already! */
-		if (is->priv->idle_state == IMAPX_IDLE_ISSUED)
+		if (is->priv->idle_state == IMAPX_IDLE_ISSUED) {
 			is->priv->idle_state = IMAPX_IDLE_STARTED;
-		else if (is->priv->idle_state == IMAPX_IDLE_CANCEL) {
+		} else if (is->priv->idle_state == IMAPX_IDLE_CANCEL) {
 			/* IDLE got cancelled after we sent the command, while
 			 * we were waiting for this continuation. Send DONE
 			 * immediately. */
 			if (!imapx_command_idle_stop (is, error)) {
-				g_mutex_unlock (&is->priv->idle_lock);
+				g_rec_mutex_unlock (&is->priv->idle_lock);
 				return FALSE;
 			}
-			is->priv->idle_state = IMAPX_IDLE_OFF;
+			is->priv->idle_state = IMAPX_IDLE_WAIT_DONE;
+		} else if (is->priv->idle_state == IMAPX_IDLE_WAIT_DONE) {
+			/* Do nothing, just wait */
 		} else {
 			c (
 				is->tagprefix, "idle starts in wrong state %d\n",
 				is->priv->idle_state);
 		}
-		g_mutex_unlock (&is->priv->idle_lock);
+		g_rec_mutex_unlock (&is->priv->idle_lock);
 
 		QUEUE_LOCK (is);
 		is->literal = NULL;
@@ -3548,9 +3551,9 @@ imapx_command_idle_done (CamelIMAPXServe
 		camel_imapx_job_take_error (job, local_error);
 	}
 
-	g_mutex_lock (&is->priv->idle_lock);
+	g_rec_mutex_lock (&is->priv->idle_lock);
 	is->priv->idle_state = IMAPX_IDLE_OFF;
-	g_mutex_unlock (&is->priv->idle_lock);
+	g_rec_mutex_unlock (&is->priv->idle_lock);
 
 	imapx_unregister_job (is, job);
 }
@@ -3579,29 +3582,23 @@ imapx_job_idle_start (CamelIMAPXJob *job
 	cp = g_queue_peek_head (&ic->parts);
 	cp->type |= CAMEL_IMAPX_COMMAND_CONTINUATION;
 
-	g_mutex_lock (&is->priv->idle_lock);
+	QUEUE_LOCK (is);
+	g_rec_mutex_lock (&is->priv->idle_lock);
 	/* Don't issue it if the idle was cancelled already */
 	if (is->priv->idle_state == IMAPX_IDLE_PENDING) {
 		is->priv->idle_state = IMAPX_IDLE_ISSUED;
-		g_mutex_unlock (&is->priv->idle_lock);
 
-		QUEUE_LOCK (is);
-		/* It can be that another thread started a command between
-		   the two locks above had been interchanged, thus also test
-		   whether the active command queue is empty, before starting
-		   the IDLE command. */
 		if (camel_imapx_command_queue_is_empty (is->active)) {
 			imapx_command_start (is, ic);
 		} else {
 			c (is->tagprefix, "finally cancelling IDLE, other command was quicker\n");
+			is->priv->idle_state = IMAPX_IDLE_OFF;
 			imapx_unregister_job (is, job);
 		}
 	} else {
-		g_mutex_unlock (&is->priv->idle_lock);
-
-		QUEUE_LOCK (is);
 		imapx_unregister_job (is, job);
 	}
+	g_rec_mutex_unlock (&is->priv->idle_lock);
 	QUEUE_UNLOCK (is);
 
 	camel_imapx_command_unref (ic);
@@ -3690,13 +3687,16 @@ imapx_call_idle (gpointer data)
 		goto exit;
 
 	/* XXX Rename to 'pending_lock'? */
-	g_mutex_lock (&is->priv->idle_lock);
+	g_rec_mutex_lock (&is->priv->idle_lock);
 	g_source_unref (is->priv->idle_pending);
 	is->priv->idle_pending = NULL;
-	g_mutex_unlock (&is->priv->idle_lock);
 
-	if (is->priv->idle_state != IMAPX_IDLE_PENDING)
+	if (is->priv->idle_state != IMAPX_IDLE_PENDING) {
+		g_rec_mutex_unlock (&is->priv->idle_lock);
 		goto exit;
+	}
+
+	g_rec_mutex_unlock (&is->priv->idle_lock);
 
 	g_mutex_lock (&is->priv->select_lock);
 	mailbox = g_weak_ref_get (&is->priv->select_mailbox);
@@ -3765,7 +3765,7 @@ imapx_idle_thread (gpointer data)
 	 *     regressions.
 	 */
 
-	g_mutex_lock (&is->priv->idle_lock);
+	g_rec_mutex_lock (&is->priv->idle_lock);
 
 	g_warn_if_fail (is->priv->idle_pending == NULL);
 	pending = g_timeout_source_new_seconds (IMAPX_IDLE_DWELL_TIME);
@@ -3778,7 +3778,7 @@ imapx_idle_thread (gpointer data)
 	is->priv->idle_pending = g_source_ref (pending);
 	g_source_unref (pending);
 
-	g_mutex_unlock (&is->priv->idle_lock);
+	g_rec_mutex_unlock (&is->priv->idle_lock);
 
 	g_main_loop_run (is->priv->idle_main_loop);
 
@@ -3798,36 +3798,40 @@ imapx_stop_idle (CamelIMAPXServer *is,
 
 	time (&now);
 
-	g_mutex_lock (&is->priv->idle_lock);
+	g_rec_mutex_lock (&is->priv->idle_lock);
 
 	switch (is->priv->idle_state) {
 		case IMAPX_IDLE_ISSUED:
 			is->priv->idle_state = IMAPX_IDLE_CANCEL;
-			/* fall through */
+			result = IMAPX_IDLE_STOP_SUCCESS;
+			break;
 
 		case IMAPX_IDLE_CANCEL:
+		case IMAPX_IDLE_WAIT_DONE:
 			result = IMAPX_IDLE_STOP_SUCCESS;
 			break;
 
 		case IMAPX_IDLE_STARTED:
 			if (imapx_command_idle_stop (is, error)) {
 				result = IMAPX_IDLE_STOP_SUCCESS;
+				is->priv->idle_state = IMAPX_IDLE_WAIT_DONE;
 			} else {
 				result = IMAPX_IDLE_STOP_ERROR;
+				is->priv->idle_state = IMAPX_IDLE_OFF;
 				goto exit;
 			}
-			/* fall through */
+			break;
 
 		case IMAPX_IDLE_PENDING:
 			is->priv->idle_state = IMAPX_IDLE_OFF;
-			/* fall through */
+			break;
 
 		case IMAPX_IDLE_OFF:
 			break;
 	}
 
 exit:
-	g_mutex_unlock (&is->priv->idle_lock);
+	g_rec_mutex_unlock (&is->priv->idle_lock);
 
 	return result;
 }
@@ -3838,9 +3842,14 @@ imapx_start_idle (CamelIMAPXServer *is)
 	if (camel_application_is_exiting)
 		return;
 
-	g_mutex_lock (&is->priv->idle_lock);
+	g_rec_mutex_lock (&is->priv->idle_lock);
+
+	if (is->priv->idle_state != IMAPX_IDLE_OFF) {
+		g_warn_if_fail (is->priv->idle_state == IMAPX_IDLE_OFF);
+		g_rec_mutex_unlock (&is->priv->idle_lock);
+		return;
+	}
 
-	g_return_if_fail (is->priv->idle_state == IMAPX_IDLE_OFF);
 	is->priv->idle_state = IMAPX_IDLE_PENDING;
 
 	if (is->priv->idle_thread == NULL) {
@@ -3861,7 +3870,7 @@ imapx_start_idle (CamelIMAPXServer *is)
 		g_source_unref (pending);
 	}
 
-	g_mutex_unlock (&is->priv->idle_lock);
+	g_rec_mutex_unlock (&is->priv->idle_lock);
 }
 
 static gboolean
@@ -3869,12 +3878,12 @@ imapx_in_idle (CamelIMAPXServer *is)
 {
 	gboolean in_idle = FALSE;
 
-	g_mutex_lock (&is->priv->idle_lock);
+	g_rec_mutex_lock (&is->priv->idle_lock);
 
 	if (is->priv->idle_thread != NULL)
 		in_idle = (is->priv->idle_state > IMAPX_IDLE_OFF);
 
-	g_mutex_unlock (&is->priv->idle_lock);
+	g_rec_mutex_unlock (&is->priv->idle_lock);
 
 	return in_idle;
 }
@@ -7809,7 +7818,7 @@ imapx_server_finalize (GObject *object)
 	g_hash_table_destroy (is->priv->known_alerts);
 	g_mutex_clear (&is->priv->known_alerts_lock);
 
-	g_mutex_clear (&is->priv->idle_lock);
+	g_rec_mutex_clear (&is->priv->idle_lock);
 	g_main_loop_unref (is->priv->idle_main_loop);
 	g_main_context_unref (is->priv->idle_main_context);
 
@@ -7993,7 +8002,7 @@ camel_imapx_server_init (CamelIMAPXServe
 
 	main_context = g_main_context_new ();
 
-	g_mutex_init (&is->priv->idle_lock);
+	g_rec_mutex_init (&is->priv->idle_lock);
 	is->priv->idle_main_loop = g_main_loop_new (main_context, FALSE);
 	is->priv->idle_main_context = g_main_context_ref (main_context);