[evolution-data-server] CamelIMAPXServer: Use a main loop in the IDLE thread.



commit ecc1e9b80a36144e14801c7baf7497121b47ec47
Author: Matthew Barnes <mbarnes redhat com>
Date:   Tue Jan 14 11:10:37 2014 -0500

    CamelIMAPXServer: Use a main loop in the IDLE thread.
    
    Instead of mucking with infinite "while" loops and condition variables,
    run a GMainLoop in the IDLE thread and attach a GSource from the parser
    thread when an IDLE command should be issued.

 camel/providers/imapx/camel-imapx-server.c |  197 ++++++++++++++-------------
 1 files changed, 102 insertions(+), 95 deletions(-)
---
diff --git a/camel/providers/imapx/camel-imapx-server.c b/camel/providers/imapx/camel-imapx-server.c
index 7c2e0bb..1e60781 100644
--- a/camel/providers/imapx/camel-imapx-server.c
+++ b/camel/providers/imapx/camel-imapx-server.c
@@ -357,12 +357,10 @@ struct _CamelIMAPXServerPrivate {
        /* IDLE support */
        GMutex idle_lock;
        GThread *idle_thread;
-       GCond idle_start_watch_cond;
-       GMutex idle_start_watch_mutex;
-       gboolean idle_start_watch_is_set;
-       time_t idle_started;
+       GMainLoop *idle_main_loop;
+       GMainContext *idle_main_context;
+       GSource *idle_pending;
        enum _idle_state idle_state;
-       gboolean idle_exit;
 };
 
 enum {
@@ -3573,100 +3571,106 @@ imapx_server_fetch_new_messages (CamelIMAPXServer *is,
        return success;
 }
 
-static gpointer
-imapx_idle_thread (gpointer data)
+static gboolean
+imapx_call_idle (gpointer data)
 {
-       CamelIMAPXServer *is = (CamelIMAPXServer *) data;
+       CamelFolder *folder;
+       CamelIMAPXServer *is;
+       CamelIMAPXMailbox *mailbox;
        GCancellable *cancellable;
        GError *local_error = NULL;
 
-       cancellable = g_weak_ref_get (&is->priv->parser_cancellable);
+       is = CAMEL_IMAPX_SERVER (data);
 
-       while (TRUE) {
-               g_mutex_lock (&is->priv->idle_start_watch_mutex);
-               is->priv->idle_start_watch_is_set = FALSE;
-               g_mutex_unlock (&is->priv->idle_start_watch_mutex);
+       /* XXX Rename to 'pending_lock'? */
+       g_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);
 
-               g_mutex_lock (&is->priv->idle_lock);
+       if (is->priv->idle_state != IMAPX_IDLE_PENDING)
+               return G_SOURCE_REMOVE;
 
-               while (TRUE) {
-                       CamelFolder *folder;
-                       CamelIMAPXMailbox *mailbox;
-                       gboolean new_messages_on_server;
-                       time_t dwelled;
+       mailbox = g_weak_ref_get (&is->priv->select_mailbox);
+       if (mailbox == NULL)
+               return G_SOURCE_REMOVE;
 
-                       if (is->priv->idle_state != IMAPX_IDLE_PENDING)
-                               break;
+       folder = imapx_server_ref_folder (is, mailbox);
+       cancellable = g_weak_ref_get (&is->priv->parser_cancellable);
 
-                       if (is->priv->idle_exit)
-                               break;
+       /* We block here until the IDLE command completes. */
+       camel_imapx_server_idle (is, mailbox, cancellable, &local_error);
 
-                       mailbox = g_weak_ref_get (&is->priv->select_mailbox);
-                       if (mailbox == NULL)
-                               break;
+       if (local_error == NULL) {
+               CamelFolder *folder;
+               gboolean have_new_messages;
+               gboolean fetch_new_messages;
 
-                       dwelled = time (NULL) - is->priv->idle_started;
-                       if (dwelled < IMAPX_IDLE_DWELL_TIME) {
-                               gulong seconds;
+               folder = imapx_server_ref_folder (is, mailbox);
 
-                               g_object_unref (mailbox);
+               have_new_messages =
+                       camel_imapx_mailbox_get_messages (mailbox) >
+                       camel_folder_summary_count (folder->summary);
 
-                               g_mutex_unlock (&is->priv->idle_lock);
-                               seconds = IMAPX_IDLE_DWELL_TIME - dwelled;
-                               g_usleep (seconds * G_USEC_PER_SEC);
-                               g_mutex_lock (&is->priv->idle_lock);
+               fetch_new_messages =
+                       have_new_messages &&
+                       imapx_is_command_queue_empty (is);
 
-                               continue;
-                       }
+               if (fetch_new_messages)
+                       imapx_server_fetch_new_messages (
+                               is, mailbox, TRUE, TRUE,
+                               cancellable, &local_error);
 
-                       g_mutex_unlock (&is->priv->idle_lock);
+               g_clear_object (&folder);
+       }
 
-                       folder = imapx_server_ref_folder (is, mailbox);
+       /* XXX Need a better way to propagate IDLE errors. */
+       if (local_error != NULL) {
+               g_warning ("%s: %s", G_STRFUNC, local_error->message);
+               g_clear_error (&local_error);
+       }
 
-                       camel_imapx_server_idle (
-                               is, mailbox, cancellable, &local_error);
+       g_clear_object (&folder);
+       g_clear_object (&cancellable);
 
-                       new_messages_on_server =
-                               camel_imapx_mailbox_get_messages (mailbox) >
-                               camel_folder_summary_count (folder->summary);
+       return G_SOURCE_REMOVE;
+}
 
-                       if (local_error == NULL &&
-                           new_messages_on_server &&
-                           imapx_is_command_queue_empty (is)) {
-                               imapx_server_fetch_new_messages (
-                                       is, mailbox, TRUE, TRUE,
-                                       cancellable, &local_error);
-                       }
+static gpointer
+imapx_idle_thread (gpointer data)
+{
+       CamelIMAPXServer *is = (CamelIMAPXServer *) data;
+       GSource *pending;
 
-                       if (local_error != NULL) {
-                               e (is->tagprefix, "Caught exception in idle thread:  %s \n", 
local_error->message);
-                               /* No way to asyncronously notify UI ? */
-                               g_clear_error (&local_error);
-                       }
+       g_main_context_push_thread_default (is->priv->idle_main_context);
 
-                       g_clear_object (&folder);
-                       g_clear_object (&mailbox);
+       /* Schedule the first IDLE command after a brief "dwell"
+        * delay so any other pending commands get priority.
+        *
+        * XXX Don't fully understand why this is necessary, but
+        *     for now just adapting old code and hoping to avoid
+        *     regressions.
+        */
 
-                       g_mutex_lock (&is->priv->idle_lock);
-               }
+       g_mutex_lock (&is->priv->idle_lock);
 
-               g_mutex_unlock (&is->priv->idle_lock);
+       g_warn_if_fail (is->priv->idle_pending == NULL);
+       pending = g_timeout_source_new_seconds (IMAPX_IDLE_DWELL_TIME);
+       g_source_set_name (pending, "imapx_call_idle");
+       g_source_set_callback (
+               pending, imapx_call_idle,
+               g_object_ref (is),
+               (GDestroyNotify) g_object_unref);
+       g_source_attach (pending, is->priv->idle_main_context);
+       is->priv->idle_pending = g_source_ref (pending);
+       g_source_unref (pending);
 
-               g_mutex_lock (&is->priv->idle_start_watch_mutex);
-               while (!is->priv->idle_start_watch_is_set)
-                       g_cond_wait (
-                               &is->priv->idle_start_watch_cond,
-                               &is->priv->idle_start_watch_mutex);
-               g_mutex_unlock (&is->priv->idle_start_watch_mutex);
+       g_mutex_unlock (&is->priv->idle_lock);
 
-               if (is->priv->idle_exit)
-                       break;
-       }
+       g_main_loop_run (is->priv->idle_main_loop);
 
-       g_object_unref (cancellable);
+       g_main_context_pop_thread_default (is->priv->idle_main_context);
 
-       g_clear_error (&local_error);
-       is->priv->idle_thread = NULL;
        return NULL;
 }
 
@@ -3724,20 +3728,14 @@ imapx_exit_idle (CamelIMAPXServer *is)
 
        g_mutex_lock (&is->priv->idle_lock);
 
-       if (is->priv->idle_thread != NULL) {
-               is->priv->idle_exit = TRUE;
-
-               g_mutex_lock (&is->priv->idle_start_watch_mutex);
-               is->priv->idle_start_watch_is_set = TRUE;
-               g_cond_broadcast (&is->priv->idle_start_watch_cond);
-               g_mutex_unlock (&is->priv->idle_start_watch_mutex);
-
-               idle_thread = is->priv->idle_thread;
-               is->priv->idle_thread = NULL;
-       }
+       idle_thread = is->priv->idle_thread;
+       is->priv->idle_thread = NULL;
 
        g_mutex_unlock (&is->priv->idle_lock);
 
+       if (g_main_loop_is_running (is->priv->idle_main_loop))
+               g_main_loop_quit (is->priv->idle_main_loop);
+
        if (idle_thread != NULL)
                g_thread_join (idle_thread);
 }
@@ -3751,20 +3749,24 @@ imapx_start_idle (CamelIMAPXServer *is)
        g_mutex_lock (&is->priv->idle_lock);
 
        g_return_if_fail (is->priv->idle_state == IMAPX_IDLE_OFF);
-       time (&is->priv->idle_started);
        is->priv->idle_state = IMAPX_IDLE_PENDING;
 
        if (is->priv->idle_thread == NULL) {
-               is->priv->idle_start_watch_is_set = FALSE;
-
                is->priv->idle_thread = g_thread_new (
                        NULL, (GThreadFunc) imapx_idle_thread, is);
 
-       } else {
-               g_mutex_lock (&is->priv->idle_start_watch_mutex);
-               is->priv->idle_start_watch_is_set = TRUE;
-               g_cond_broadcast (&is->priv->idle_start_watch_cond);
-               g_mutex_unlock (&is->priv->idle_start_watch_mutex);
+       } else if (is->priv->idle_pending == NULL) {
+               GSource *pending;
+
+               pending = g_idle_source_new ();
+               g_source_set_name (pending, "imapx_call_idle");
+               g_source_set_callback (
+                       pending, imapx_call_idle,
+                       g_object_ref (is),
+                       (GDestroyNotify) g_object_unref);
+               g_source_attach (pending, is->priv->idle_main_context);
+               is->priv->idle_pending = g_source_ref (pending);
+               g_source_unref (pending);
        }
 
        g_mutex_unlock (&is->priv->idle_lock);
@@ -7406,8 +7408,8 @@ imapx_server_finalize (GObject *object)
        g_mutex_clear (&is->priv->known_alerts_lock);
 
        g_mutex_clear (&is->priv->idle_lock);
-       g_cond_clear (&is->priv->idle_start_watch_cond);
-       g_mutex_clear (&is->priv->idle_start_watch_mutex);
+       g_main_loop_unref (is->priv->idle_main_loop);
+       g_main_context_unref (is->priv->idle_main_context);
 
        /* Chain up to parent's finalize() method. */
        G_OBJECT_CLASS (camel_imapx_server_parent_class)->finalize (object);
@@ -7591,6 +7593,7 @@ static void
 camel_imapx_server_init (CamelIMAPXServer *is)
 {
        GHashTable *mailboxes;
+       GMainContext *main_context;
 
        /* Hash table key is owned by the CamelIMAPXMailbox. */
        mailboxes = g_hash_table_new_full (
@@ -7631,9 +7634,13 @@ camel_imapx_server_init (CamelIMAPXServer *is)
 
        /* Initialize IDLE structs. */
 
+       main_context = g_main_context_new ();
+
        g_mutex_init (&is->priv->idle_lock);
-       g_cond_init (&is->priv->idle_start_watch_cond);
-       g_mutex_init (&is->priv->idle_start_watch_mutex);
+       is->priv->idle_main_loop = g_main_loop_new (main_context, FALSE);
+       is->priv->idle_main_context = g_main_context_ref (main_context);
+
+       g_main_context_unref (main_context);
 }
 
 CamelIMAPXServer *


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