diff -up evolution-data-server-3.8.5/camel/camel-imapx-conn-manager.c.imapx-error-cancelled-message-download evolution-data-server-3.8.5/camel/camel-imapx-conn-manager.c --- evolution-data-server-3.8.5/camel/camel-imapx-conn-manager.c.imapx-error-cancelled-message-download 2014-05-16 11:36:17.872612953 +0200 +++ evolution-data-server-3.8.5/camel/camel-imapx-conn-manager.c 2014-05-16 11:36:17.884612846 +0200 @@ -883,3 +883,21 @@ camel_imapx_conn_manager_close_connectio g_list_free_full (connections, (GDestroyNotify) connection_info_cancel_and_unref); } +/* for debugging purposes only */ +void +camel_imapx_conn_manager_dump_queue_status (CamelIMAPXConnManager *con_man) +{ + GList *list, *link; + + g_return_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (con_man)); + + list = imapx_conn_manager_list_info (con_man); + + for (link = list; link != NULL; link = g_list_next (link)) { + ConnectionInfo *cinfo = link->data; + camel_imapx_server_dump_queue_status (cinfo->is); + connection_info_unref (cinfo); + } + + g_list_free (list); +} diff -up evolution-data-server-3.8.5/camel/camel-imapx-conn-manager.h.imapx-error-cancelled-message-download evolution-data-server-3.8.5/camel/camel-imapx-conn-manager.h --- evolution-data-server-3.8.5/camel/camel-imapx-conn-manager.h.imapx-error-cancelled-message-download 2014-05-16 11:36:17.873612944 +0200 +++ evolution-data-server-3.8.5/camel/camel-imapx-conn-manager.h 2014-05-16 11:36:17.884612846 +0200 @@ -84,6 +84,10 @@ void camel_imapx_conn_manager_update_co CamelIMAPXServer *server, const gchar *folder_name); +/* for debugging purposes only */ +void camel_imapx_conn_manager_dump_queue_status + (CamelIMAPXConnManager *con_man); + G_END_DECLS #endif /* _CAMEL_IMAPX_SERVER_H */ diff -up evolution-data-server-3.8.5/camel/camel-imapx-job.h.imapx-error-cancelled-message-download evolution-data-server-3.8.5/camel/camel-imapx-job.h --- evolution-data-server-3.8.5/camel/camel-imapx-job.h.imapx-error-cancelled-message-download 2014-05-16 11:36:17.798613612 +0200 +++ evolution-data-server-3.8.5/camel/camel-imapx-job.h 2014-05-16 11:36:17.884612846 +0200 @@ -55,7 +55,7 @@ struct _CamelIMAPXJob { guint noreply:1; /* dont wait for reply */ guint32 type; /* operation type */ gint pri; /* the command priority */ - gshort commands; /* counts how many commands are outstanding */ + volatile gint commands; /* counts how many commands are outstanding */ }; CamelIMAPXJob * camel_imapx_job_new (GCancellable *cancellable); diff -up evolution-data-server-3.8.5/camel/camel-imapx-server.c.imapx-error-cancelled-message-download evolution-data-server-3.8.5/camel/camel-imapx-server.c --- evolution-data-server-3.8.5/camel/camel-imapx-server.c.imapx-error-cancelled-message-download 2014-05-16 11:36:17.879612890 +0200 +++ evolution-data-server-3.8.5/camel/camel-imapx-server.c 2014-05-16 11:36:17.885612837 +0200 @@ -989,9 +989,7 @@ imapx_uidset_add (struct _uidset_state * /* Must hold QUEUE_LOCK */ static gboolean imapx_command_start (CamelIMAPXServer *is, - CamelIMAPXCommand *ic, - GCancellable *cancellable, - GError **error) + CamelIMAPXCommand *ic) { CamelIMAPXStream *stream = NULL; CamelIMAPXCommandPart *cp; @@ -1002,6 +1000,7 @@ imapx_command_start (CamelIMAPXServer *i gboolean success = FALSE; gchar *string; gint retval; + GCancellable *cancellable; GError *local_error = NULL; camel_imapx_command_close (ic); @@ -1024,7 +1023,11 @@ imapx_command_start (CamelIMAPXServer *i imapx_server_command_added (is, ic); job = camel_imapx_command_get_job (ic); - if (job && g_cancellable_set_error_if_cancelled (camel_imapx_job_get_cancellable (job), &local_error)) { + if (job) + cancellable = camel_imapx_job_get_cancellable (job); + else + cancellable = NULL; + if (job && g_cancellable_set_error_if_cancelled (cancellable, &local_error)) { camel_imapx_job_set_error (job, local_error); g_clear_error (&local_error); goto err; @@ -1034,7 +1037,7 @@ imapx_command_start (CamelIMAPXServer *i if (stream == NULL) { g_set_error ( - error, CAMEL_IMAPX_ERROR, 1, + &local_error, CAMEL_IMAPX_ERROR, 1, "Cannot issue command, no stream available"); goto err; } @@ -1052,7 +1055,7 @@ imapx_command_start (CamelIMAPXServer *i string = g_strdup_printf ( "%c%05u %s\r\n", is->tagprefix, ic->tag, cp->data); retval = camel_stream_write_string ( - CAMEL_STREAM (stream), string, cancellable, error); + CAMEL_STREAM (stream), string, cancellable, &local_error); g_free (string); if (retval == -1) @@ -1060,7 +1063,7 @@ imapx_command_start (CamelIMAPXServer *i while (is->literal == ic && cp_literal_plus) { /* Sent LITERAL+ continuation immediately */ - if (!imapx_continuation (is, stream, TRUE, cancellable, error)) + if (!imapx_continuation (is, stream, TRUE, cancellable, &local_error)) goto err; } @@ -1080,6 +1083,10 @@ err: if (ic->status->result == IMAPX_OK) ic->status->result = IMAPX_UNKNOWN; + if (job && local_error) { + camel_imapx_job_set_error (job, local_error); + g_clear_error (&local_error); + } /* Send a NULL GError since we've already set a * GError to get here, and we're not interested * in individual command errors. */ @@ -1134,9 +1141,7 @@ duplicate_fetch_or_refresh (CamelIMAPXSe * must have QUEUE lock */ static gboolean -imapx_command_start_next (CamelIMAPXServer *is, - GCancellable *cancellable, - GError **error) +imapx_command_start_next (CamelIMAPXServer *is) { CamelIMAPXCommand *first_ic; CamelFolder *folder; @@ -1151,7 +1156,7 @@ imapx_command_start_next (CamelIMAPXServ folder = g_weak_ref_get (&is->select_pending); if (folder != NULL) { - GQueue start = G_QUEUE_INIT; + CamelIMAPXCommand *start_ic = NULL; GList *head, *link; c (is->tagprefix, "-- Checking job queue for non-folder jobs\n"); @@ -1159,7 +1164,7 @@ imapx_command_start_next (CamelIMAPXServ head = camel_imapx_command_queue_peek_head_link (is->queue); /* Tag which commands in the queue to start. */ - for (link = head; link != NULL; link = g_list_next (link)) { + for (link = head; link != NULL && !start_ic; link = g_list_next (link)) { CamelIMAPXCommand *ic = link->data; if (ic->pri < min_pri) @@ -1169,37 +1174,23 @@ imapx_command_start_next (CamelIMAPXServ if (!ic->select) { c (is->tagprefix, "--> starting '%s'\n", ic->name); min_pri = ic->pri; - g_queue_push_tail (&start, link); - } - if (g_queue_get_length (&start) == MAX_COMMANDS) - break; + /* Each command must be removed from 'is->queue' before + * starting it, so we temporarily reference the command + * to avoid accidentally finalizing it. */ + start_ic = camel_imapx_command_ref (ic); + } } - if (g_queue_is_empty (&start)) + if (!start_ic) c (is->tagprefix, "* no, waiting for pending select '%s'\n", camel_folder_get_full_name (folder)); - /* Start the tagged commands. - * - * Each command must be removed from 'is->queue' before - * starting it, so we temporarily reference the command - * to avoid accidentally finalizing it. */ - while ((link = g_queue_pop_head (&start)) != NULL) { - CamelIMAPXCommand *ic; - - ic = camel_imapx_command_ref (link->data); - camel_imapx_command_queue_delete_link (is->queue, link); - imapx_server_command_removed (is, ic); - - success = imapx_command_start ( - is, ic, cancellable, error); - - camel_imapx_command_unref (ic); - - if (!success) { - g_queue_clear (&start); - break; - } + /* Start the tagged command */ + if (start_ic) { + camel_imapx_command_queue_remove (is->queue, start_ic); + imapx_server_command_removed (is, start_ic); + imapx_command_start (is, start_ic); + camel_imapx_command_unref (start_ic); } g_clear_object (&folder); @@ -1219,7 +1210,7 @@ imapx_command_start_next (CamelIMAPXServ if (stream != NULL) { stop_result = imapx_stop_idle ( - is, stream, cancellable, error); + is, stream, is->cancellable, NULL); g_object_unref (stream); } @@ -1255,9 +1246,8 @@ imapx_command_start_next (CamelIMAPXServ /* See if any queued jobs on this select first */ folder = g_weak_ref_get (&is->select_folder); if (folder != NULL) { - GQueue start = G_QUEUE_INIT; + CamelIMAPXCommand *start_ic = NULL; GList *head, *link; - gboolean commands_started = FALSE; c ( is->tagprefix, "- we're selected on '%s', current jobs?\n", @@ -1284,7 +1274,7 @@ imapx_command_start_next (CamelIMAPXServ head = camel_imapx_command_queue_peek_head_link (is->queue); /* Tag which commands in the queue to start. */ - for (link = head; link != NULL; link = g_list_next (link)) { + for (link = head; link != NULL && !start_ic; link = g_list_next (link)) { CamelIMAPXCommand *ic = link->data; if (is->literal != NULL) @@ -1299,48 +1289,29 @@ imapx_command_start_next (CamelIMAPXServ !duplicate_fetch_or_refresh (is, ic))) { c (is->tagprefix, "--> starting '%s'\n", ic->name); min_pri = ic->pri; - g_queue_push_tail (&start, link); + /* Each command must be removed from 'is->queue' before + * starting it, so we temporarily reference the command + * to avoid accidentally finalizing it. */ + start_ic = camel_imapx_command_ref (ic); } else { /* This job isn't for the selected folder, but we don't want to * consider jobs with _lower_ priority than this, even if they * are for the selected folder. */ min_pri = ic->pri; } - - if (g_queue_get_length (&start) == MAX_COMMANDS) - break; } g_clear_object (&folder); - /* Start the tagged commands. - * - * Each command must be removed from 'is->queue' before - * starting it, so we temporarily reference the command - * to avoid accidentally finalizing it. */ - while ((link = g_queue_pop_head (&start)) != NULL) { - CamelIMAPXCommand *ic; - gboolean success; + /* Start the tagged command */ + if (start_ic) { + camel_imapx_command_queue_remove (is->queue, start_ic); + imapx_server_command_removed (is, start_ic); + imapx_command_start (is, start_ic); + camel_imapx_command_unref (start_ic); - ic = camel_imapx_command_ref (link->data); - camel_imapx_command_queue_delete_link (is->queue, link); - imapx_server_command_removed (is, ic); - - success = imapx_command_start ( - is, ic, cancellable, error); - - camel_imapx_command_unref (ic); - - if (!success) { - g_queue_clear (&start); - return FALSE; - } - - commands_started = TRUE; - } - - if (commands_started) return TRUE; + } } /* This won't be NULL because we checked for an empty queue above. */ @@ -1349,13 +1320,19 @@ imapx_command_start_next (CamelIMAPXServ /* If we need to select a folder for the first command, do it now, * once it is complete it will re-call us if it succeeded. */ if (first_ic->select) { + GError *local_error = NULL; + CamelIMAPXJob *job; c ( is->tagprefix, "Selecting folder '%s' for command '%s'(%p)\n", camel_folder_get_full_name (first_ic->select), first_ic->name, first_ic); - imapx_select (is, first_ic->select, FALSE, cancellable, error); + job = camel_imapx_command_get_job (first_ic); + imapx_select (is, first_ic->select, FALSE, job ? camel_imapx_job_get_cancellable (job) : NULL, &local_error); + if (local_error && job) + camel_imapx_job_set_error (job, local_error); + g_clear_error (&local_error); } else { - GQueue start = G_QUEUE_INIT; + CamelIMAPXCommand *start_ic = NULL; GList *head, *link; min_pri = first_ic->pri; @@ -1365,7 +1342,7 @@ imapx_command_start_next (CamelIMAPXServ head = camel_imapx_command_queue_peek_head_link (is->queue); /* Tag which commands in the queue to start. */ - for (link = head; link != NULL; link = g_list_next (link)) { + for (link = head; link != NULL && !start_ic; link = g_list_next (link)) { CamelIMAPXCommand *ic = link->data; if (is->literal != NULL) @@ -1378,37 +1355,21 @@ imapx_command_start_next (CamelIMAPXServ !duplicate_fetch_or_refresh (is, ic))) { c (is->tagprefix, "* queueing job %3d '%s'\n", (gint) ic->pri, ic->name); min_pri = ic->pri; - g_queue_push_tail (&start, link); + /* Each command must be removed from 'is->queue' before + * starting it, so we temporarily reference the command + * to avoid accidentally finalizing it. */ + start_ic = camel_imapx_command_ref (ic); } - - if (g_queue_get_length (&start) == MAX_COMMANDS) - break; } g_clear_object (&folder); - /* Start the tagged commands. - * - * Each command must be removed from 'is->queue' before - * starting it, so we temporarily reference the command - * to avoid accidentally finalizing it. */ - while ((link = g_queue_pop_head (&start)) != NULL) { - CamelIMAPXCommand *ic; - gboolean success; - - ic = camel_imapx_command_ref (link->data); - camel_imapx_command_queue_delete_link (is->queue, link); - imapx_server_command_removed (is, ic); - - success = imapx_command_start ( - is, ic, cancellable, error); - - camel_imapx_command_unref (ic); - - if (!success) { - g_queue_clear (&start); - return FALSE; - } + /* Start the tagged command */ + if (start_ic) { + camel_imapx_command_queue_remove (is->queue, start_ic); + imapx_server_command_removed (is, start_ic); + imapx_command_start (is, start_ic); + camel_imapx_command_unref (start_ic); } } @@ -1430,7 +1391,6 @@ imapx_is_command_queue_empty (CamelIMAPX static gboolean imapx_command_queue (CamelIMAPXServer *is, CamelIMAPXCommand *ic, - GCancellable *cancellable, GError **error) { CamelIMAPXJob *job; @@ -1472,7 +1432,7 @@ imapx_command_queue (CamelIMAPXServer *i camel_imapx_command_queue_insert_sorted (is->queue, ic); imapx_server_command_added (is, ic); - success = imapx_command_start_next (is, cancellable, error); + success = imapx_command_start_next (is); QUEUE_UNLOCK (is); @@ -2808,7 +2768,7 @@ imapx_continuation (CamelIMAPXServer *is QUEUE_LOCK (is); is->literal = NULL; - success = imapx_command_start_next (is, cancellable, error); + success = imapx_command_start_next (is); QUEUE_UNLOCK (is); return success; @@ -2950,7 +2910,7 @@ noskip: is->literal = newliteral; if (!litplus) - success = imapx_command_start_next (is, cancellable, error); + success = imapx_command_start_next (is); QUEUE_UNLOCK (is); return success; @@ -3063,7 +3023,7 @@ imapx_completion (CamelIMAPXServer *is, camel_imapx_command_unref (ic); QUEUE_LOCK (is); - success = imapx_command_start_next (is, cancellable, error); + success = imapx_command_start_next (is); QUEUE_UNLOCK (is); return success; @@ -3129,7 +3089,7 @@ imapx_command_run (CamelIMAPXServer *is, camel_imapx_command_close (ic); QUEUE_LOCK (is); - imapx_command_start (is, ic, cancellable, error); + imapx_command_start (is, ic); QUEUE_UNLOCK (is); while (success && ic->status == NULL) @@ -3200,7 +3160,7 @@ imapx_command_run_sync (CamelIMAPXServer /* Unref'ed in imapx_command_complete(). */ camel_imapx_command_ref (ic); - success = imapx_command_queue (is, ic, cancellable, error); + success = imapx_command_queue (is, ic, error); if (success) camel_imapx_command_wait (ic); @@ -3252,10 +3212,14 @@ imapx_unregister_job (CamelIMAPXServer * camel_imapx_job_done (job); QUEUE_LOCK (is); + if (g_queue_remove (&is->jobs, job)) { imapx_server_job_removed (is, job); camel_imapx_job_unref (job); } + + imapx_command_start_next (is); + QUEUE_UNLOCK (is); } @@ -3361,7 +3325,7 @@ imapx_job_idle_start (CamelIMAPXJob *job /* Don't issue it if the idle was cancelled already */ if (is->idle->state == IMAPX_IDLE_PENDING) { is->idle->state = IMAPX_IDLE_ISSUED; - success = imapx_command_start (is, ic, cancellable, error); + success = imapx_command_start (is, ic); } else { imapx_unregister_job (is, job); camel_imapx_command_unref (ic); @@ -3903,7 +3867,7 @@ imapx_select (CamelIMAPXServer *is, camel_imapx_command_add_qresync_parameter (ic, folder); ic->complete = imapx_command_select_done; - imapx_command_start (is, ic, cancellable, error); + imapx_command_start (is, ic); return TRUE; } @@ -4579,6 +4543,7 @@ imapx_command_fetch_message_done (CamelI CamelFolder *folder; GetMessageData *data; CamelIMAPXFolder *ifolder; + GError *local_error = NULL; gboolean success = TRUE; job = camel_imapx_command_get_job (ic); @@ -4594,11 +4559,11 @@ imapx_command_fetch_message_done (CamelI * or we failed. Failure is handled in the fetch code, so * we just return the job, or keep it alive with more requests */ - job->commands--; + g_atomic_int_add (&job->commands, -1); - if (camel_imapx_command_set_error_if_failed (ic, cancellable, error)) { + if (camel_imapx_command_set_error_if_failed (ic, cancellable, &local_error)) { g_prefix_error ( - error, "%s: ", + &local_error, "%s: ", _("Error fetching message")); data->body_len = -1; success = FALSE; @@ -4627,18 +4592,24 @@ imapx_command_fetch_message_done (CamelI camel_imapx_command_set_job (new_ic, job); new_ic->pri = job->pri - 1; data->fetch_offset += MULTI_SIZE; - job->commands++; + g_atomic_int_add (&job->commands, 1); success = imapx_command_queue ( - is, new_ic, cancellable, error); + is, new_ic, &local_error); goto exit; } } /* If we have more messages to fetch, skip the rest. */ - if (job->commands > 0) + if (g_atomic_int_get (&job->commands) > 0) { + /* Make sure no command will starve in a queue */ + QUEUE_LOCK (is); + imapx_command_start_next (is); + QUEUE_UNLOCK (is); + goto exit; + } /* No more messages to fetch, let's wrap things up. */ @@ -4646,24 +4617,24 @@ imapx_command_fetch_message_done (CamelI if (success) { success = camel_stream_flush ( - data->stream, cancellable, error) == 0; + data->stream, cancellable, &local_error) == 0; g_prefix_error ( - error, "%s: ", + &local_error, "%s: ", _("Failed to close the tmp stream")); } if (success) { success = camel_stream_close ( - data->stream, cancellable, error) == 0; + data->stream, cancellable, &local_error) == 0; g_prefix_error ( - error, "%s: ", + &local_error, "%s: ", _("Failed to close the tmp stream")); } - if (success && g_cancellable_set_error_if_cancelled (cancellable, error)) { + if (success && g_cancellable_set_error_if_cancelled (cancellable, &local_error)) { success = FALSE; g_prefix_error ( - error, "%s: ", + &local_error, "%s: ", _("Error fetching message")); } @@ -4686,11 +4657,11 @@ imapx_command_fetch_message_done (CamelI /* Exchange the "tmp" stream for the "cur" stream. */ g_clear_object (&data->stream); data->stream = camel_data_cache_get ( - ifolder->cache, "cur", data->uid, error); + ifolder->cache, "cur", data->uid, &local_error); success = (data->stream != NULL); } else { g_set_error ( - error, G_FILE_ERROR, + &local_error, G_FILE_ERROR, g_file_error_from_errno (errno), "%s: %s", _("Failed to copy the tmp file"), @@ -4702,7 +4673,20 @@ imapx_command_fetch_message_done (CamelI g_free (tmp_filename); } - camel_data_cache_remove (ifolder->cache, "tmp", data->uid, NULL); + /* Delete the 'tmp' file only if the operation wasn't cancelled. It's because + cancelled operations end before they are properly finished (IMAP-protocol speaking), + thus if any other GET_MESSAGE operation was waiting for this job, then it + realized that the message was not downloaded and opened its own "tmp" file, but + of the same name, thus this remove would drop file which could be used + by a different GET_MESSAGE job. */ + if (!g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + camel_data_cache_remove (ifolder->cache, "tmp", data->uid, NULL); + + /* Avoid possible use-after-free when the imapx_unregister_job() can + also free the 'job' structure. */ + if (local_error != NULL) + camel_imapx_job_set_error (job, local_error); + imapx_unregister_job (is, job); exit: @@ -4710,6 +4694,9 @@ exit: camel_imapx_command_unref (ic); + if (local_error) + g_propagate_error (error, local_error); + return success; } @@ -4743,10 +4730,10 @@ imapx_job_get_message_start (CamelIMAPXJ camel_imapx_command_set_job (ic, job); ic->pri = job->pri; data->fetch_offset += MULTI_SIZE; - job->commands++; + g_atomic_int_add (&job->commands, 1); success = imapx_command_queue ( - is, ic, cancellable, error); + is, ic, error); if (!success) break; } @@ -4758,9 +4745,9 @@ imapx_job_get_message_start (CamelIMAPXJ ic->complete = imapx_command_fetch_message_done; camel_imapx_command_set_job (ic, job); ic->pri = job->pri; - job->commands++; + g_atomic_int_add (&job->commands, 1); - success = imapx_command_queue (is, ic, cancellable, error); + success = imapx_command_queue (is, ic, error); } g_object_unref (folder); @@ -4903,14 +4890,14 @@ imapx_command_copy_messages_step_start ( if (res == 1) { camel_imapx_command_add (ic, " %f", data->dest); data->index = i + 1; - return imapx_command_queue (is, ic, cancellable, error); + return imapx_command_queue (is, ic, error); } } data->index = i; if (imapx_uidset_done (&data->uidset, ic)) { camel_imapx_command_add (ic, " %f", data->dest); - return imapx_command_queue (is, ic, cancellable, error); + return imapx_command_queue (is, ic, error); } return TRUE; @@ -5055,11 +5042,11 @@ imapx_job_append_message_start (CamelIMA ic->complete = imapx_command_append_message_done; camel_imapx_command_set_job (ic, job); ic->pri = job->pri; - job->commands++; + g_atomic_int_add (&job->commands, 1); g_object_unref (folder); - return imapx_command_queue (is, ic, cancellable, error); + return imapx_command_queue (is, ic, error); } /* ********************************************************************** */ @@ -5233,7 +5220,7 @@ imapx_command_step_fetch_done (CamelIMAP g_object_unref (folder); - return imapx_command_queue (is, ic, cancellable, error); + return imapx_command_queue (is, ic, error); } } } @@ -5245,7 +5232,7 @@ imapx_command_step_fetch_done (CamelIMAP g_object_unref (folder); - return imapx_command_queue (is, ic, cancellable, error); + return imapx_command_queue (is, ic, error); } } @@ -5556,7 +5543,7 @@ imapx_job_scan_changes_start (CamelIMAPX g_object_unref (folder); - return imapx_command_queue (is, ic, cancellable, error); + return imapx_command_queue (is, ic, error); } static gboolean @@ -5730,7 +5717,7 @@ imapx_job_fetch_new_messages_start (Came g_object_unref (folder); - return imapx_command_queue (is, ic, cancellable, error); + return imapx_command_queue (is, ic, error); } static gboolean @@ -5863,7 +5850,7 @@ imapx_job_fetch_messages_start (CamelIMA g_object_unref (folder); - return imapx_command_queue (is, ic, cancellable, error); + return imapx_command_queue (is, ic, error); } static gboolean @@ -6208,7 +6195,7 @@ imapx_job_expunge_start (CamelIMAPXJob * ic->pri = job->pri; ic->complete = imapx_command_expunge_done; - success = imapx_command_queue (is, ic, cancellable, error); + success = imapx_command_queue (is, ic, error); } g_object_unref (folder); @@ -6279,7 +6266,7 @@ imapx_job_list_start (CamelIMAPXJob *job camel_imapx_command_set_job (ic, job); ic->complete = imapx_command_list_done; - return imapx_command_queue (is, ic, cancellable, error); + return imapx_command_queue (is, ic, error); } static gboolean @@ -6368,7 +6355,7 @@ imapx_job_manage_subscription_start (Cam g_object_unref (store); - return imapx_command_queue (is, ic, cancellable, error); + return imapx_command_queue (is, ic, error); } /* ********************************************************************** */ @@ -6422,7 +6409,7 @@ imapx_job_create_folder_start (CamelIMAP g_free (encoded_fname); - return imapx_command_queue (is, ic, cancellable, error); + return imapx_command_queue (is, ic, error); } /* ********************************************************************** */ @@ -6487,7 +6474,7 @@ imapx_job_delete_folder_start (CamelIMAP camel_imapx_command_set_job (ic, job); ic->complete = imapx_command_delete_folder_done; - success = imapx_command_queue (is, ic, cancellable, error); + success = imapx_command_queue (is, ic, error); g_object_unref (folder); } @@ -6561,7 +6548,7 @@ imapx_job_rename_folder_start (CamelIMAP camel_imapx_command_set_job (ic, job); ic->complete = imapx_command_rename_folder_done; - success = imapx_command_queue (is, ic, cancellable, error); + success = imapx_command_queue (is, ic, error); g_object_unref (folder); } @@ -6628,7 +6615,7 @@ imapx_job_update_quota_info_start (Camel camel_imapx_command_set_job (ic, job); ic->complete = imapx_command_update_quota_info_done; - success = imapx_command_queue (is, ic, cancellable, error); + success = imapx_command_queue (is, ic, error); g_free (encoded_folder_name); @@ -6699,7 +6686,7 @@ imapx_job_uid_search_start (CamelIMAPXJo g_object_unref (folder); - return imapx_command_queue (is, ic, cancellable, error); + return imapx_command_queue (is, ic, error); } /* ********************************************************************** */ @@ -6752,7 +6739,7 @@ imapx_job_noop_start (CamelIMAPXJob *job ic->pri = IMAPX_PRIORITY_NOOP; } - return imapx_command_queue (is, ic, cancellable, error); + return imapx_command_queue (is, ic, error); } /* ********************************************************************** */ @@ -6810,7 +6797,7 @@ imapx_command_sync_changes_done (CamelIM mobile_mode = camel_imapx_settings_get_mobile_mode (settings); g_object_unref (settings); - job->commands--; + g_atomic_int_add (&job->commands, -1); full_name = camel_folder_get_full_name (folder); parent_store = camel_folder_get_parent_store (folder); @@ -6862,7 +6849,7 @@ imapx_command_sync_changes_done (CamelIM ((CamelIMAPXFolder *) folder)->unread_on_server += data->unread_change; } - if (job->commands == 0) { + if (g_atomic_int_get (&job->commands) == 0) { if (folder->summary && (folder->summary->flags & CAMEL_FOLDER_SUMMARY_DIRTY) != 0) { CamelStoreInfo *si; @@ -6888,6 +6875,11 @@ imapx_command_sync_changes_done (CamelIM camel_store_summary_save ((CamelStoreSummary *)((CamelIMAPXStore *) parent_store)->summary); imapx_unregister_job (is, job); + } else { + /* Make sure no command will starve in a queue */ + QUEUE_LOCK (is); + imapx_command_start_next (is); + QUEUE_UNLOCK (is); } g_object_unref (folder); @@ -6974,9 +6966,9 @@ imapx_job_sync_changes_start (CamelIMAPX send = imapx_uidset_add (&ss, ic, camel_message_info_uid (info)); } if (send == 1 || (i == uids->len - 1 && imapx_uidset_done (&ss, ic))) { - job->commands++; + g_atomic_int_add (&job->commands, 1); camel_imapx_command_add (ic, " %tFLAGS.SILENT (%t)", on?"+":"-", flags_table[j].name); - if (!imapx_command_queue (is, ic, cancellable, error)) { + if (!imapx_command_queue (is, ic, error)) { camel_message_info_free (info); goto exit; } @@ -7015,9 +7007,9 @@ imapx_job_sync_changes_start (CamelIMAPX if (imapx_uidset_add (&ss, ic, camel_message_info_uid (info)) == 1 || (i == c->infos->len - 1 && imapx_uidset_done (&ss, ic))) { - job->commands++; + g_atomic_int_add (&job->commands, 1); camel_imapx_command_add (ic, " %tFLAGS.SILENT (%t)", on?"+":"-", c->name); - if (!imapx_command_queue (is, ic, cancellable, error)) + if (!imapx_command_queue (is, ic, error)) goto exit; ic = NULL; } @@ -7029,11 +7021,14 @@ imapx_job_sync_changes_start (CamelIMAPX exit: g_object_unref (folder); - /* Since this may start in another thread ... we need to - * lock the commands count, ho hum */ - - if (job->commands == 0) + if (g_atomic_int_get (&job->commands) == 0) { imapx_unregister_job (is, job); + } else { + /* Make sure no command will starve in a queue */ + QUEUE_LOCK (is); + imapx_command_start_next (is); + QUEUE_UNLOCK (is); + } return TRUE; } @@ -7701,6 +7696,11 @@ imapx_server_get_message (CamelIMAPXServ return NULL; } + /* This makes sure that if any file is left on the disk, it is not reused. + That can happen when the previous message download had been cancelled + or finished with an error. */ + camel_data_cache_remove (ifolder->cache, "tmp", uid, NULL); + data = g_slice_new0 (GetMessageData); data->uid = g_strdup (uid); data->stream = camel_data_cache_add (ifolder->cache, "tmp", uid, NULL); @@ -8964,3 +8964,106 @@ camel_imapx_server_shutdown (CamelIMAPXS g_clear_object (&cancellable); g_clear_error (&shutdown_error_copy); } + +static const gchar * +imapx_server_get_job_type_name (CamelIMAPXJob *job) +{ + if (!job) + return "[null]"; + + switch (job->type) { + case IMAPX_JOB_GET_MESSAGE: + return "GET_MESSAGE"; + case IMAPX_JOB_APPEND_MESSAGE: + return "APPEND_MESSAGE"; + case IMAPX_JOB_COPY_MESSAGE: + return "COPY_MESSAGE"; + case IMAPX_JOB_FETCH_NEW_MESSAGES: + return "FETCH_NEW_MESSAGES"; + case IMAPX_JOB_REFRESH_INFO: + return "REFRESH_INFO"; + case IMAPX_JOB_SYNC_CHANGES: + return "SYNC_CHANGES"; + case IMAPX_JOB_EXPUNGE: + return "EXPUNGE"; + case IMAPX_JOB_NOOP: + return "NOOP"; + case IMAPX_JOB_IDLE: + return "IDLE"; + case IMAPX_JOB_LIST: + return "LIST"; + case IMAPX_JOB_CREATE_FOLDER: + return "CREATE_FOLDER"; + case IMAPX_JOB_DELETE_FOLDER: + return "DELETE_FOLDER"; + case IMAPX_JOB_RENAME_FOLDER: + return "RENAME_FOLDER"; + case IMAPX_JOB_MANAGE_SUBSCRIPTION: + return "MANAGE_SUBSCRIPTION"; + case IMAPX_JOB_UPDATE_QUOTA_INFO: + return "UPDATE_QUOTA_INFO"; + case IMAPX_JOB_UID_SEARCH: + return "UID_SEARCH"; + } + + return "???"; +} + +static void +imapx_server_dump_one_queue (CamelIMAPXCommandQueue *queue, + const gchar *queue_name) +{ + GList *iter; + gint ii; + + g_return_if_fail (queue != NULL); + g_return_if_fail (queue_name != NULL); + + if (camel_imapx_command_queue_is_empty (queue)) + return; + + printf (" Content of '%s':\n", queue_name); + + for (ii = 0, iter = camel_imapx_command_queue_peek_head_link (queue); iter != NULL; iter = g_list_next (iter), ii++) { + CamelIMAPXCommand *ic = iter->data; + CamelIMAPXJob *job = camel_imapx_command_get_job (ic); + + printf (" [%d] command:%p for job:%p (type:0x%x %s)\n", ii, ic, job, job ? job->type : 0, imapx_server_get_job_type_name (job)); + } +} + +/* for debugging purposes only */ +void +camel_imapx_server_dump_queue_status (CamelIMAPXServer *imapx_server) +{ + g_return_if_fail (CAMEL_IS_IMAPX_SERVER (imapx_server)); + + QUEUE_LOCK (imapx_server); + + printf (" Queue status for server %p: jobs:%d queued:%d active:%d done:%d\n", imapx_server, + g_queue_get_length (&imapx_server->jobs), + camel_imapx_command_queue_get_length (imapx_server->queue), + camel_imapx_command_queue_get_length (imapx_server->active), + camel_imapx_command_queue_get_length (imapx_server->done)); + + if (!g_queue_is_empty (&imapx_server->jobs)) { + GList *iter; + gint ii; + + printf (" Content of 'jobs':\n"); + + for (ii = 0, iter = g_queue_peek_head_link (&imapx_server->jobs); iter != NULL; iter = g_list_next (iter), ii++) { + CamelIMAPXJob *job = iter->data; + + printf (" [%d] job:%p (type:0x%x %s) with pending commands:%d\n", ii, job, job ? job->type : 0, + imapx_server_get_job_type_name (job), + job ? g_atomic_int_get (&job->commands) : -1); + } + } + + imapx_server_dump_one_queue (imapx_server->queue, "queue"); + imapx_server_dump_one_queue (imapx_server->active, "active"); + imapx_server_dump_one_queue (imapx_server->done, "done"); + + QUEUE_UNLOCK (imapx_server); +} diff -up evolution-data-server-3.8.5/camel/camel-imapx-server.h.imapx-error-cancelled-message-download evolution-data-server-3.8.5/camel/camel-imapx-server.h --- evolution-data-server-3.8.5/camel/camel-imapx-server.h.imapx-error-cancelled-message-download 2014-05-16 11:36:17.875612926 +0200 +++ evolution-data-server-3.8.5/camel/camel-imapx-server.h 2014-05-16 11:36:17.885612837 +0200 @@ -312,6 +312,9 @@ struct _CamelIMAPXJob * CamelFolder *folder, guint32 job_type, const gchar *uid); +/* for debugging purposes only */ +void camel_imapx_server_dump_queue_status + (CamelIMAPXServer *imapx_server); G_END_DECLS diff -up evolution-data-server-3.8.5/camel/camel-imapx-store.c.imapx-error-cancelled-message-download evolution-data-server-3.8.5/camel/camel-imapx-store.c --- evolution-data-server-3.8.5/camel/camel-imapx-store.c.imapx-error-cancelled-message-download 2014-05-16 11:36:17.876612917 +0200 +++ evolution-data-server-3.8.5/camel/camel-imapx-store.c 2014-05-16 11:36:17.886612828 +0200 @@ -2226,3 +2226,12 @@ camel_imapx_store_ref_job (CamelIMAPXSto return job; } + +/* for debugging purposes only */ +void +camel_imapx_store_dump_queue_status (CamelIMAPXStore *imapx_store) +{ + g_return_if_fail (CAMEL_IS_IMAPX_STORE (imapx_store)); + + camel_imapx_conn_manager_dump_queue_status (imapx_store->con_man); +} diff -up evolution-data-server-3.8.5/camel/camel-imapx-store.h.imapx-error-cancelled-message-download evolution-data-server-3.8.5/camel/camel-imapx-store.h --- evolution-data-server-3.8.5/camel/camel-imapx-store.h.imapx-error-cancelled-message-download 2014-05-16 11:36:17.876612917 +0200 +++ evolution-data-server-3.8.5/camel/camel-imapx-store.h 2014-05-16 11:36:17.886612828 +0200 @@ -120,6 +120,9 @@ struct _CamelIMAPXJob * CamelFolder *folder, guint32 job_type, const gchar *uid); +/* for debugging purposes only */ +void camel_imapx_store_dump_queue_status + (CamelIMAPXStore *imapx_store); G_END_DECLS