[balsa] Use a GThreadPool to open mailboxes



commit 6a412847814d764f5821dbe571f35ff156765150
Author: Peter Bloomfield <PeterBloomfield bellsouth net>
Date:   Tue Feb 18 10:22:05 2020 -0500

    Use a GThreadPool to open mailboxes
    
    * src/balsa-index.c
      (balsa_index_set_mailbox_node): new function, called in a thread;
      (balsa_index_load_mailbox_node): mailbox-node has already been set;
    * src/balsa-index.h: export balsa_index_set_mailbox_node;
    * src/main-window.c
      (balsa_window_class_init): rename destroy to dispose;
      (balsa_window_init): create the GThreadPool, and a GPtrArray
        to hold the task info structures;
      (balsa_window_new): make priv->preview a weak pointer;
      (bw_real_open_mbnode_idle_cb): we no longer need to guard
        against being called after the main window has been
        destroyed;
      (bw_open_mbnode_info_free): a new cleanup function to be
        passed to g_idle_add_full();
      (bw_real_open_mbnode_thread): GThreadPool manages the GThread
        instances, so we no longer need a GMutex; we no longer need
        to guard against being called after the main window has been
        destroyed;
      (balsa_window_real_open_mbnode): push the task to the thread
        pool instead of creating a new GThread;
      (balsa_window_dispose): free the thread pool and the pointer
        array;
      (bw_add_mbox_to_checklist): cosmetics.

 ChangeLog         |  28 ++++++++++
 src/balsa-index.c |  41 ++++++++-------
 src/balsa-index.h |   5 +-
 src/main-window.c | 149 ++++++++++++++++++++++++++----------------------------
 4 files changed, 125 insertions(+), 98 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index f501d2f35..24ce23ab6 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,31 @@
+2020-02-18  Peter Bloomfield  <pbloomfield bellsouth net>
+
+       Use a GThreadPool to open mailboxes
+
+       * src/balsa-index.c
+         (balsa_index_set_mailbox_node): new function, called in a thread;
+         (balsa_index_load_mailbox_node): mailbox-node has already been set;
+       * src/balsa-index.h: export balsa_index_set_mailbox_node;
+       * src/main-window.c
+         (balsa_window_class_init): rename destroy to dispose;
+         (balsa_window_init): create the GThreadPool, and a GPtrArray
+           to hold the task info structures;
+         (balsa_window_new): make priv->preview a weak pointer;
+         (bw_real_open_mbnode_idle_cb): we no longer need to guard
+           against being called after the main window has been
+           destroyed;
+         (bw_open_mbnode_info_free): a new cleanup function to be
+           passed to g_idle_add_full();
+         (bw_real_open_mbnode_thread): GThreadPool manages the GThread
+           instances, so we no longer need a GMutex; we no longer need
+           to guard against being called after the main window has been
+           destroyed;
+         (balsa_window_real_open_mbnode): push the task to the thread
+           pool instead of creating a new GThread;
+         (balsa_window_dispose): free the thread pool and the pointer
+           array;
+         (bw_add_mbox_to_checklist): cosmetics.
+
 2020-02-16  Albrecht Dreß  <albrecht dress arcor de>
 
        imap-handle: Do not crash on PREAUTH greeting
diff --git a/src/balsa-index.c b/src/balsa-index.c
index 60b319c2a..fca4d30e3 100644
--- a/src/balsa-index.c
+++ b/src/balsa-index.c
@@ -951,31 +951,38 @@ bndx_mailbox_message_expunged_cb(LibBalsaMailbox * mailbox, guint msgno,
         --bindex->next_msgno;
 }
 
-/* balsa_index_load_mailbox_node:
+/* balsa_index_set_mailbox_node:
  *
+ * Called in a thread, so do not call Gtk;
  * balsa_mailbox_node_get_mailbox(mbnode) is already open
  */
-
-gboolean
-balsa_index_load_mailbox_node(BalsaIndex * index,
-                              BalsaMailboxNode* mbnode)
+void
+balsa_index_set_mailbox_node(BalsaIndex * bindex,
+                             BalsaMailboxNode* mbnode)
 {
-    GtkTreeView *tree_view;
-    LibBalsaMailbox *mailbox;
-
-    g_return_val_if_fail(BALSA_IS_INDEX(index), TRUE);
-    g_return_val_if_fail(index->mailbox_node == NULL, TRUE);
-    g_return_val_if_fail(BALSA_IS_MAILBOX_NODE(mbnode), TRUE);
-
-    mailbox = balsa_mailbox_node_get_mailbox(mbnode);
-    g_return_val_if_fail(LIBBALSA_IS_MAILBOX(mailbox), TRUE);
+    g_return_if_fail(BALSA_IS_INDEX(bindex));
+    g_return_if_fail(bindex->mailbox_node == NULL);
+    g_return_if_fail(BALSA_IS_MAILBOX_NODE(mbnode));
 
     /*
      * set the new mailbox
      */
-    index->mailbox_node = mbnode;
+    bindex->mailbox_node = mbnode;
     g_object_weak_ref(G_OBJECT(mbnode),
-                      (GWeakNotify) bndx_mbnode_weak_notify, index);
+                      (GWeakNotify) bndx_mbnode_weak_notify, bindex);
+}
+
+
+void
+balsa_index_load_mailbox_node(BalsaIndex * index)
+{
+    LibBalsaMailbox *mailbox;
+    GtkTreeView *tree_view;
+
+    g_return_if_fail(BALSA_IS_INDEX(index));
+
+    mailbox = balsa_mailbox_node_get_mailbox(index->mailbox_node);
+
     /*
      * rename "from" column to "to" for outgoing mail
      */
@@ -1008,8 +1015,6 @@ balsa_index_load_mailbox_node(BalsaIndex * index,
     index->search_iter = libbalsa_mailbox_search_iter_new(cond_undeleted);
     /* Note when this mailbox was opened, for use in auto-closing. */
     balsa_mailbox_node_set_last_use_time(index->mailbox_node);
-
-    return FALSE;
 }
 
 void
diff --git a/src/balsa-index.h b/src/balsa-index.h
index 409c05a33..f489aa6af 100644
--- a/src/balsa-index.h
+++ b/src/balsa-index.h
@@ -56,8 +56,9 @@ G_DECLARE_FINAL_TYPE(BalsaIndex, balsa_index, BALSA, INDEX, GtkTreeView)
 
 /* sets the mail stream; if it's a new stream, then it's 
  * contents is loaded into the index */
-    gboolean balsa_index_load_mailbox_node(BalsaIndex * bindex,
-                                           BalsaMailboxNode * mbnode);
+    void balsa_index_set_mailbox_node(BalsaIndex       * bindex,
+                                      BalsaMailboxNode * mbnode);
+    void balsa_index_load_mailbox_node(BalsaIndex * bindex);
     void balsa_index_set_width_preference(BalsaIndex *bindex,
                                           BalsaIndexWidthPreference pref);
     void balsa_index_scroll_on_open(BalsaIndex *index);
diff --git a/src/main-window.c b/src/main-window.c
index ffb586279..d489580d2 100644
--- a/src/main-window.c
+++ b/src/main-window.c
@@ -109,7 +109,7 @@ static void balsa_window_real_open_mbnode(BalsaWindow *window,
                                           gboolean set_current);
 static void balsa_window_real_close_mbnode(BalsaWindow *window,
                                           BalsaMailboxNode *mbnode);
-static void balsa_window_destroy(GObject * object);
+static void balsa_window_dispose(GObject * object);
 
 static gboolean bw_close_mailbox_on_timer(BalsaWindow * window);
 
@@ -176,6 +176,8 @@ static void bw_notebook_page_notify_cb(GtkWidget  *child,
 static GtkWidget *bw_notebook_label_new (BalsaMailboxNode* mbnode);
 static void bw_reset_filter(BalsaWindow * bw);
 
+typedef struct _BalsaWindowRealOpenMbnodeInfo BalsaWindowRealOpenMbnodeInfo;
+static void bw_real_open_mbnode_thread(BalsaWindowRealOpenMbnodeInfo * info);
 
 /* ===================================================================
    Balsa menus. Touchpad has some simplified menus which do not
@@ -224,6 +226,9 @@ struct _BalsaWindowPrivate {
     time_t last_check_time;
     guint network_changed_source_id;
     gulong network_changed_handler_id;
+
+    GThreadPool *open_mbnode_thread_pool;
+    GPtrArray *open_mbnode_info_array;
 };
 
 G_DEFINE_TYPE_WITH_PRIVATE(BalsaWindow, balsa_window, GTK_TYPE_APPLICATION_WINDOW)
@@ -246,7 +251,7 @@ balsa_window_class_init(BalsaWindowClass * klass)
                      NULL, NULL,
                      NULL, G_TYPE_NONE, 0);
 
-    object_class->dispose = balsa_window_destroy;
+    object_class->dispose = balsa_window_dispose;
 
     klass->open_mbnode  = balsa_window_real_open_mbnode;
     klass->close_mbnode = balsa_window_real_close_mbnode;
@@ -306,6 +311,11 @@ balsa_window_init(BalsaWindow * window)
         g_signal_connect(monitor, "network-changed",
                          G_CALLBACK(bw_network_changed_cb), window);
     priv->last_check_time = 0;
+    priv->open_mbnode_thread_pool =
+        g_thread_pool_new((GFunc) bw_real_open_mbnode_thread, NULL,
+                          1 /* g_get_num_processors()? */, FALSE, NULL);
+    priv->open_mbnode_info_array =
+        g_ptr_array_new_with_free_func((GDestroyNotify) g_idle_remove_by_data);
 }
 
 static gboolean
@@ -2302,6 +2312,8 @@ balsa_window_new(GtkApplication *application)
                              (gpointer *) &balsa_app.notebook);
 
     priv->preview = balsa_message_new();
+    g_object_add_weak_pointer(G_OBJECT(priv->preview),
+                              (gpointer *) &priv->preview);
     gtk_widget_hide(priv->preview);
 
     g_signal_connect(priv->preview, "select-part",
@@ -2852,14 +2864,14 @@ bw_notebook_label_new(BalsaMailboxNode * mbnode)
  * balsa_window_real_open_mbnode
  */
 
-typedef struct {
+struct _BalsaWindowRealOpenMbnodeInfo
+{
     BalsaIndex       *index;
     BalsaMailboxNode *mbnode;
     BalsaWindow      *window;
     gchar            *message;
     gboolean          set_current;
-    GApplication     *application;
-} BalsaWindowRealOpenMbnodeInfo;
+};
 
 static gboolean
 bw_real_open_mbnode_idle_cb(BalsaWindowRealOpenMbnodeInfo * info)
@@ -2874,37 +2886,21 @@ bw_real_open_mbnode_idle_cb(BalsaWindowRealOpenMbnodeInfo * info)
     gint               page_num;
     LibBalsaCondition *filter;
 
-    if (window == NULL) {
-        g_free(info->message);
-        g_object_unref(g_object_ref_sink(index));
-        g_object_unref(mbnode);
-        g_application_release(info->application);
-        g_free(info);
+    if (mbnode == NULL)
         return FALSE;
-    }
 
     balsa_window_decrease_activity(window, info->message);
-    g_object_remove_weak_pointer(G_OBJECT(window),
-                                 (gpointer *) &info->window);
-    g_free(info->message);
 
-    if (balsa_find_notebook_page_num(mailbox) >= 0) {
-        g_object_unref(g_object_ref_sink(index));
-        g_object_unref(mbnode);
-        g_application_release(info->application);
-        g_free(info);
-        return FALSE;
-    }
+    g_free(info->message);
+    info->message = NULL;
 
-    balsa_index_load_mailbox_node(index, mbnode);
+    balsa_index_load_mailbox_node(index);
 
     g_signal_connect(index, "index-changed",
                      G_CALLBACK(bw_index_changed_cb), window);
 
     label = bw_notebook_label_new(mbnode);
-    g_object_unref(mbnode);
 
-    /* store for easy access */
     scroll = gtk_scrolled_window_new(NULL, NULL);
     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),
                                    GTK_POLICY_AUTOMATIC,
@@ -2918,10 +2914,11 @@ bw_real_open_mbnode_idle_cb(BalsaWindowRealOpenMbnodeInfo * info)
     gtk_notebook_set_tab_reorderable(GTK_NOTEBOOK(priv->notebook),
                                      scroll, TRUE);
 
-    if (info->set_current)
+    if (info->set_current) {
         /* change the page to the newly selected notebook item */
         gtk_notebook_set_current_page(GTK_NOTEBOOK(priv->notebook),
                                       page_num);
+    }
 
     libbalsa_mailbox_set_threading(mailbox);
 
@@ -2934,68 +2931,57 @@ bw_real_open_mbnode_idle_cb(BalsaWindowRealOpenMbnodeInfo * info)
      * without being shown first. */
     balsa_index_scroll_on_open(index);
 
-    g_application_release(info->application);
-    g_free(info);
+    g_ptr_array_remove_fast(priv->open_mbnode_info_array, info);
 
     return FALSE;
 }
 
+static void
+bw_open_mbnode_info_free(BalsaWindowRealOpenMbnodeInfo * info)
+{
+    g_free(info->message);
+    g_object_unref(g_object_ref_sink(info->index));
+    if (info->mbnode != NULL)
+        g_object_remove_weak_pointer(G_OBJECT(info->mbnode), (gpointer *) &info->mbnode);
+    g_free(info);
+}
+
 static void
 bw_real_open_mbnode_thread(BalsaWindowRealOpenMbnodeInfo * info)
 {
-    static GMutex open_lock;
     gint try_cnt;
     LibBalsaMailbox *mailbox = balsa_mailbox_node_get_mailbox(info->mbnode);
     GError *err = NULL;
     gboolean successp;
 
-    /* Use a mutex to ensure we open only one mailbox at a time */
-    g_mutex_lock(&open_lock);
-
-    if (info->window == NULL) {
-        g_application_release(info->application);
-        g_free(info);
-        g_mutex_unlock(&open_lock);
-        return;
-    }
-
     try_cnt = 0;
     do {
         g_clear_error(&err);
         successp = libbalsa_mailbox_open(mailbox, &err);
 
-        if (info->window == NULL) {
-            g_application_release(info->application);
-            g_free(info);
-            g_mutex_unlock(&open_lock);
-            return;
-        }
-
         if(successp) break;
         if(err && err->code != LIBBALSA_MAILBOX_TOOMANYOPEN_ERROR)
             break;
         balsa_mblist_close_lru_peer_mbx(balsa_app.mblist, mailbox);
     } while(try_cnt++<3);
 
-    if (successp) {
-        g_idle_add((GSourceFunc) bw_real_open_mbnode_idle_cb, info);
+    if (successp && balsa_find_notebook_page_num(mailbox) < 0) {
+        BalsaWindowPrivate *priv =
+            balsa_window_get_instance_private(info->window);
+
+        g_ptr_array_add(priv->open_mbnode_info_array, info);
+        balsa_index_set_mailbox_node(info->index, info->mbnode);
+        g_idle_add_full(G_PRIORITY_DEFAULT_IDLE,
+                        (GSourceFunc) bw_real_open_mbnode_idle_cb, info,
+                        (GDestroyNotify) bw_open_mbnode_info_free);
     } else {
         libbalsa_information(
             LIBBALSA_INFORMATION_ERROR,
             _("Unable to Open Mailbox!\n%s."),
            err ? err->message : _("Unknown error"));
-        if (info->window) {
-            balsa_window_decrease_activity(info->window, info->message);
-            g_object_remove_weak_pointer(G_OBJECT(info->window),
-                                         (gpointer *) &info->window);
-        }
-        g_free(info->message);
-        g_object_unref(g_object_ref_sink(info->index));
-        g_object_unref(info->mbnode);
-        g_application_release(info->application);
-        g_free(info);
+        balsa_window_decrease_activity(info->window, info->message);
+        bw_open_mbnode_info_free(info);
     }
-    g_mutex_unlock(&open_lock);
 }
 
 static void
@@ -3003,10 +2989,10 @@ balsa_window_real_open_mbnode(BalsaWindow * window,
                               BalsaMailboxNode * mbnode,
                               gboolean set_current)
 {
+    BalsaWindowPrivate *priv = balsa_window_get_instance_private(window);
     BalsaIndex * index;
     gchar *message;
     LibBalsaMailbox *mailbox;
-    GThread *open_thread;
     BalsaWindowRealOpenMbnodeInfo *info;
 
     if (bw_is_open_mailbox(mailbox = balsa_mailbox_node_get_mailbox(mbnode)))
@@ -3024,22 +3010,15 @@ balsa_window_real_open_mbnode(BalsaWindow * window,
 
     info = g_new(BalsaWindowRealOpenMbnodeInfo, 1);
 
-    info->window = window;
-    g_object_add_weak_pointer(G_OBJECT(window), (gpointer *) &info->window);
+    info->mbnode = mbnode;
+    g_object_add_weak_pointer(G_OBJECT(mbnode), (gpointer *) &info->mbnode);
 
-    info->mbnode = g_object_ref(mbnode);
+    info->window = window;
     info->set_current = set_current;
     info->index = index;
     info->message = message;
 
-    info->application = G_APPLICATION(gtk_window_get_application(GTK_WINDOW(window)));
-    g_application_hold(info->application);
-
-    open_thread =
-       g_thread_new("bw_real_open_mbnode_thread",
-                                (GThreadFunc) bw_real_open_mbnode_thread,
-                                        info);
-    g_thread_unref(open_thread);
+    g_thread_pool_push(priv->open_mbnode_thread_pool, info, NULL);
 }
 
 /* balsa_window_real_close_mbnode:
@@ -3167,14 +3146,15 @@ bw_close_mailbox_on_timer(BalsaWindow * window)
 }
 
 static void
-balsa_window_destroy(GObject * object)
+balsa_window_dispose(GObject * object)
 {
     BalsaWindow *window = BALSA_WINDOW(object);
     BalsaWindowPrivate *priv = balsa_window_get_instance_private(window);
 
-    /* The preview window seems to get finalized without notification;
-     * we no longer need it, so we just drop our pointer: */
-    priv->preview = NULL;
+    if (priv->preview != NULL) {
+        g_object_remove_weak_pointer(G_OBJECT(priv->preview), (gpointer *) &priv->preview);
+        priv->preview = NULL;
+    }
 
     if (priv->set_message_id != 0) {
         g_source_remove(priv->set_message_id);
@@ -3192,13 +3172,24 @@ balsa_window_destroy(GObject * object)
         priv->network_changed_handler_id = 0;
     }
 
+    if (priv->open_mbnode_thread_pool != NULL) {
+        g_thread_pool_free(priv->open_mbnode_thread_pool,
+                           TRUE /* pool should shut down immediately */,
+                           TRUE /* wait for all tasks to be finished */);
+        priv->open_mbnode_thread_pool = NULL;
+    }
+
+    if (priv->open_mbnode_info_array != NULL) {
+        g_ptr_array_free(priv->open_mbnode_info_array, TRUE);
+        priv->open_mbnode_info_array = NULL;
+    }
+
     balsa_app.in_destruction = TRUE;
     G_OBJECT_CLASS(balsa_window_parent_class)->dispose(object);
 
     balsa_unregister_pixmaps();
 }
 
-
 /*
  * refresh data in the main window
  */
@@ -3357,9 +3348,11 @@ bw_add_mbox_to_checklist(GtkTreeModel * model, GtkTreePath * path,
        if (!LIBBALSA_IS_MAILBOX_IMAP(mailbox) ||
            bw_imap_check_test(balsa_mailbox_node_get_dir(mbnode) ? balsa_mailbox_node_get_dir(mbnode) :
                            libbalsa_mailbox_imap_get_path
-                           (LIBBALSA_MAILBOX_IMAP(mailbox))))
-           *list = g_slist_prepend(*list, g_object_ref(mailbox));
+                           (LIBBALSA_MAILBOX_IMAP(mailbox)))) {
+            *list = g_slist_prepend(*list, g_object_ref(mailbox));
+        }
     }
+
     g_object_unref(mbnode);
 
     return FALSE;


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