[evolution-data-server] Bug 737468 - [IMAPx] Temporarily workaround 'Empty cache file' error



commit 687d1b1bcba2f9a1a42f8c8e794cd17c1373f9c1
Author: Milan Crha <mcrha redhat com>
Date:   Tue Mar 3 18:36:45 2015 +0100

    Bug 737468 - [IMAPx] Temporarily workaround 'Empty cache file' error

 camel/providers/imapx/camel-imapx-job.c    |   24 ++++----
 camel/providers/imapx/camel-imapx-server.c |   84 ++++++++++++++++++++++++----
 camel/providers/imapx/camel-imapx-server.h |    2 +-
 camel/providers/imapx/camel-imapx-store.c  |   21 +++++++-
 camel/providers/imapx/camel-imapx-store.h  |    4 +
 5 files changed, 109 insertions(+), 26 deletions(-)
---
diff --git a/camel/providers/imapx/camel-imapx-job.c b/camel/providers/imapx/camel-imapx-job.c
index 8f69b9f..7c79496 100644
--- a/camel/providers/imapx/camel-imapx-job.c
+++ b/camel/providers/imapx/camel-imapx-job.c
@@ -182,6 +182,7 @@ camel_imapx_job_wait (CamelIMAPXJob *job,
 {
        CamelIMAPXRealJob *real_job;
        GCancellable *cancellable;
+       gulong cancel_id = 0;
        gboolean success = TRUE;
 
        g_return_val_if_fail (CAMEL_IS_IMAPX_JOB (job), FALSE);
@@ -189,13 +190,23 @@ camel_imapx_job_wait (CamelIMAPXJob *job,
        real_job = (CamelIMAPXRealJob *) job;
        cancellable = camel_imapx_job_get_cancellable (job);
 
+       if (G_IS_CANCELLABLE (cancellable))
+               cancel_id = g_cancellable_connect (
+                       cancellable,
+                       G_CALLBACK (imapx_job_cancelled_cb),
+                       camel_imapx_job_ref (job),
+                       (GDestroyNotify) camel_imapx_job_unref);
+
        g_mutex_lock (&real_job->done_mutex);
-       while (!real_job->done_flag)
+       while (!real_job->done_flag && !g_cancellable_is_cancelled (cancellable))
                g_cond_wait (
                        &real_job->done_cond,
                        &real_job->done_mutex);
        g_mutex_unlock (&real_job->done_mutex);
 
+       if (cancel_id > 0)
+               g_cancellable_disconnect (cancellable, cancel_id);
+
        /* Cancellation takes priority over other errors. */
        if (g_cancellable_set_error_if_cancelled (cancellable, error)) {
                success = FALSE;
@@ -233,7 +244,6 @@ camel_imapx_job_run (CamelIMAPXJob *job,
                      GError **error)
 {
        GCancellable *cancellable;
-       gulong cancel_id = 0;
        gboolean success;
 
        g_return_val_if_fail (CAMEL_IS_IMAPX_JOB (job), FALSE);
@@ -245,21 +255,11 @@ camel_imapx_job_run (CamelIMAPXJob *job,
        if (g_cancellable_set_error_if_cancelled (cancellable, error))
                return FALSE;
 
-       if (G_IS_CANCELLABLE (cancellable))
-               cancel_id = g_cancellable_connect (
-                       cancellable,
-                       G_CALLBACK (imapx_job_cancelled_cb),
-                       camel_imapx_job_ref (job),
-                       (GDestroyNotify) camel_imapx_job_unref);
-
        success = job->start (job, is, cancellable, error);
 
        if (success && !job->noreply)
                success = camel_imapx_job_wait (job, error);
 
-       if (cancel_id > 0)
-               g_cancellable_disconnect (cancellable, cancel_id);
-
        return success;
 }
 
diff --git a/camel/providers/imapx/camel-imapx-server.c b/camel/providers/imapx/camel-imapx-server.c
index cf5aa15..1d59f02 100644
--- a/camel/providers/imapx/camel-imapx-server.c
+++ b/camel/providers/imapx/camel-imapx-server.c
@@ -55,8 +55,27 @@
 #define c(...) camel_imapx_debug(command, __VA_ARGS__)
 #define e(...) camel_imapx_debug(extra, __VA_ARGS__)
 
-#define QUEUE_LOCK(x) (g_rec_mutex_lock(&(x)->queue_lock))
-#define QUEUE_UNLOCK(x) (g_rec_mutex_unlock(&(x)->queue_lock))
+#define QUEUE_LOCK(x) G_STMT_START { \
+       CamelIMAPXStore *imapx_store = camel_imapx_server_ref_store ((x)); \
+       \
+       if (imapx_store) { \
+               camel_imapx_store_job_queue_lock (imapx_store); \
+               g_object_unref (imapx_store); \
+       } \
+       \
+       g_rec_mutex_lock (&(x)->queue_lock); \
+       } G_STMT_END
+
+#define QUEUE_UNLOCK(x) G_STMT_START { \
+       CamelIMAPXStore *imapx_store = camel_imapx_server_ref_store ((x)); \
+       \
+       g_rec_mutex_unlock (&(x)->queue_lock); \
+       \
+       if (imapx_store) { \
+               camel_imapx_store_job_queue_unlock (imapx_store); \
+               g_object_unref (imapx_store); \
+       } \
+       } G_STMT_END
 
 /* Try pipelining fetch requests, 'in bits' */
 #define MULTI_SIZE (32768 * 8)
@@ -5162,23 +5181,54 @@ imapx_command_fetch_message_done (CamelIMAPXServer *is,
                g_free (tmp_filename);
        }
 
-       /* Delete the 'tmp' file only if the operation wasn't cancelled. It's because
+       /* Delete the 'tmp' file only if the operation succeeded. 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))
+       if (!local_error && !g_cancellable_is_cancelled (cancellable))
                camel_data_cache_remove (data->message_cache, "tmp", data->uid, NULL);
 
        /* Avoid possible use-after-free when the imapx_unregister_job() can
           also free the 'job' structure. */
+       camel_imapx_job_ref (job);
+
+       imapx_unregister_job (is, job);
+
        if (local_error != NULL) {
-               camel_imapx_job_take_error (job, local_error);
-               local_error = NULL;
+               CamelIMAPXJob *pending_job;
+
+               /* Give a chance to other threads. */
+               g_thread_yield ();
+
+               pending_job = imapx_server_ref_job (is, mailbox, IMAPX_JOB_GET_MESSAGE, data->uid);
+               if (pending_job != NULL) {
+                       GIOStream *cache_stream;
+
+                       /* Wait for the job to finish. */
+                       camel_imapx_job_wait (pending_job, NULL);
+                       camel_imapx_job_unref (pending_job);
+
+                       /* Disregard errors here.  If we failed to retrieve the
+                        * message from cache (implying the job we were waiting
+                        * on failed or got cancelled), we'll just re-fetch it. */
+                       cache_stream = camel_data_cache_get (data->message_cache, "cur", data->uid, NULL);
+                       if (cache_stream != NULL) {
+                               g_clear_error (&local_error);
+
+                               g_clear_object (&data->stream);
+                               data->stream = cache_stream;
+                       }
+               }
+
+               if (local_error) {
+                       camel_imapx_job_take_error (job, local_error);
+                       local_error = NULL;
+               }
        }
 
-       imapx_unregister_job (is, job);
+       camel_imapx_job_unref (job);
 
 exit:
        if (local_error != NULL)
@@ -8313,9 +8363,7 @@ imapx_server_get_message (CamelIMAPXServer *is,
        GetMessageData *data;
        gboolean registered;
 
-       job = imapx_server_ref_job (is, mailbox, IMAPX_JOB_GET_MESSAGE, message_uid);
-
-       if (job != NULL) {
+       while (job = imapx_server_ref_job (is, mailbox, IMAPX_JOB_GET_MESSAGE, message_uid), job != NULL) {
                /* Promote the existing GET_MESSAGE
                 * job's priority if ours is higher. */
                if (pri > job->pri)
@@ -8331,14 +8379,26 @@ imapx_server_get_message (CamelIMAPXServer *is,
                cache_stream = camel_data_cache_get (
                        message_cache, "cur", message_uid, NULL);
                if (cache_stream != NULL) {
-                       stream = camel_stream_new (cache_stream);
+                       /* Return new file stream, instead of a DataCache's to not fight
+                          on its content and position with other jobs, if any. */
+                       gchar *filename = camel_data_cache_get_filename (message_cache, "cur", message_uid);
+                       stream = camel_stream_fs_new_with_name (filename, O_RDONLY, 0, NULL);
+                       g_free (filename);
                        g_object_unref (cache_stream);
-                       return stream;
+
+                       if (stream)
+                               return stream;
                }
        }
 
        QUEUE_LOCK (is);
 
+       if (g_cancellable_set_error_if_cancelled (cancellable, error)) {
+               QUEUE_UNLOCK (is);
+
+               return NULL;
+       }
+
        mi = camel_folder_summary_get (summary, message_uid);
        if (mi == NULL) {
                g_set_error (
diff --git a/camel/providers/imapx/camel-imapx-server.h b/camel/providers/imapx/camel-imapx-server.h
index 448b813..b7b876a 100644
--- a/camel/providers/imapx/camel-imapx-server.h
+++ b/camel/providers/imapx/camel-imapx-server.h
@@ -118,7 +118,7 @@ struct _CamelIMAPXServer {
 
        /* Current command/work queue.  All commands are stored in one list,
         * all the time, so they can be cleaned up in exception cases */
-       GRecMutex queue_lock;
+       GRecMutex queue_lock; /* use in combination with camel_imapx_store_job_queue_lock()/_unlock() */
        CamelIMAPXCommand *literal;
        CamelIMAPXCommandQueue *queue;
        CamelIMAPXCommandQueue *active;
diff --git a/camel/providers/imapx/camel-imapx-store.c b/camel/providers/imapx/camel-imapx-store.c
index 8e21f49..13111b6 100644
--- a/camel/providers/imapx/camel-imapx-store.c
+++ b/camel/providers/imapx/camel-imapx-store.c
@@ -64,6 +64,8 @@ struct _CamelIMAPXStorePrivate {
        CamelIMAPXServer *connecting_server;
        gboolean is_concurrent_connection;
 
+       GRecMutex job_queue_lock;
+
        GMutex server_lock;
 
        GHashTable *quota_info;
@@ -703,8 +705,8 @@ imapx_store_finalize (GObject *object)
 
        priv = CAMEL_IMAPX_STORE_GET_PRIVATE (object);
 
+       g_rec_mutex_clear (&priv->job_queue_lock);
        g_mutex_clear (&priv->get_finfo_lock);
-
        g_mutex_clear (&priv->server_lock);
 
        g_hash_table_destroy (priv->quota_info);
@@ -2669,6 +2671,7 @@ camel_imapx_store_init (CamelIMAPXStore *store)
 
        store->priv->con_man = camel_imapx_conn_manager_new (CAMEL_STORE (store));
 
+       g_rec_mutex_init (&store->priv->job_queue_lock);
        g_mutex_init (&store->priv->get_finfo_lock);
 
        g_mutex_init (&store->priv->namespaces_lock);
@@ -3463,6 +3466,22 @@ camel_imapx_store_ref_job (CamelIMAPXStore *imapx_store,
        return job;
 }
 
+void
+camel_imapx_store_job_queue_lock (CamelIMAPXStore *imapx_store)
+{
+       g_return_if_fail (CAMEL_IS_IMAPX_STORE (imapx_store));
+
+       g_rec_mutex_lock (&imapx_store->priv->job_queue_lock);
+}
+
+void
+camel_imapx_store_job_queue_unlock (CamelIMAPXStore *imapx_store)
+{
+       g_return_if_fail (CAMEL_IS_IMAPX_STORE (imapx_store));
+
+       g_rec_mutex_unlock (&imapx_store->priv->job_queue_lock);
+}
+
 /* for debugging purposes only */
 void
 camel_imapx_store_dump_queue_status (CamelIMAPXStore *imapx_store)
diff --git a/camel/providers/imapx/camel-imapx-store.h b/camel/providers/imapx/camel-imapx-store.h
index f1311c2..b5e07d6 100644
--- a/camel/providers/imapx/camel-imapx-store.h
+++ b/camel/providers/imapx/camel-imapx-store.h
@@ -131,6 +131,10 @@ struct _CamelIMAPXJob *
                                                 CamelIMAPXMailbox *mailbox,
                                                 guint32 job_type,
                                                 const gchar *uid);
+void           camel_imapx_store_job_queue_lock
+                                               (CamelIMAPXStore *imapx_store);
+void           camel_imapx_store_job_queue_unlock
+                                               (CamelIMAPXStore *imapx_store);
 
 /* for debugging purposes only */
 void           camel_imapx_store_dump_queue_status


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