[evolution] Bug 788156 - Remote content download slows down message preview



commit aff71954924b8e428f5bc54c0072bae1e5a5afde
Author: Milan Crha <mcrha redhat com>
Date:   Tue Oct 3 11:43:19 2017 +0200

    Bug 788156 - Remote content download slows down message preview

 src/e-util/e-content-request.c              |   53 +++++++++------
 src/e-util/e-simple-async-result.c          |   99 +++++++++++++++++++++++++++
 src/e-util/e-simple-async-result.h          |   12 +++-
 src/e-util/e-web-view.c                     |   38 ++++++++++-
 src/modules/webkit-editor/e-webkit-editor.c |    5 +-
 src/shell/main.c                            |    1 +
 6 files changed, 185 insertions(+), 23 deletions(-)
---
diff --git a/src/e-util/e-content-request.c b/src/e-util/e-content-request.c
index 3e95094..9be6e09 100644
--- a/src/e-util/e-content-request.c
+++ b/src/e-util/e-content-request.c
@@ -20,7 +20,8 @@
 
 #include <glib.h>
 #include <glib-object.h>
-#include <gio/gio.h>
+
+#include "e-simple-async-result.h"
 
 #include "e-content-request.h"
 
@@ -90,6 +91,8 @@ typedef struct _ThreadData
        GInputStream *out_stream;
        gint64 out_stream_length;
        gchar *out_mime_type;
+       GError *error;
+       gboolean success;
 } ThreadData;
 
 static void
@@ -102,29 +105,27 @@ thread_data_free (gpointer ptr)
                g_clear_object (&td->requester);
                g_free (td->uri);
                g_free (td->out_mime_type);
+               g_clear_error (&td->error);
                g_free (td);
        }
 }
 
 static void
-content_request_process_thread (GTask *task,
+content_request_process_thread (ESimpleAsyncResult *result,
                                gpointer source_object,
-                               gpointer task_data,
                                GCancellable *cancellable)
 {
-       ThreadData *td = task_data;
-       GError *local_error = NULL;
+       ThreadData *td;
 
+       g_return_if_fail (E_IS_SIMPLE_ASYNC_RESULT (result));
        g_return_if_fail (E_IS_CONTENT_REQUEST (source_object));
+
+       td = e_simple_async_result_get_user_data (result);
        g_return_if_fail (td != NULL);
 
-       if (!e_content_request_process_sync (E_CONTENT_REQUEST (source_object),
+       td->success = e_content_request_process_sync (E_CONTENT_REQUEST (source_object),
                td->uri, td->requester, &td->out_stream, &td->out_stream_length, &td->out_mime_type,
-               cancellable, &local_error)) {
-               g_task_return_error (task, local_error);
-       } else {
-               g_task_return_boolean (task, TRUE);
-       }
+               cancellable, &td->error);
 }
 
 void
@@ -135,21 +136,27 @@ e_content_request_process (EContentRequest *request,
                           GAsyncReadyCallback callback,
                           gpointer user_data)
 {
-       GTask *task;
        ThreadData *td;
+       ESimpleAsyncResult *result;
+       gboolean is_http;
 
        g_return_if_fail (E_IS_CONTENT_REQUEST (request));
        g_return_if_fail (uri != NULL);
        g_return_if_fail (G_IS_OBJECT (requester));
 
+       is_http = g_ascii_strncasecmp (uri, "http", 4) == 0 ||
+                 g_ascii_strncasecmp (uri, "evo-http", 8) == 0;
+
        td = g_new0 (ThreadData, 1);
        td->uri = g_strdup (uri);
        td->requester = g_object_ref (requester);
 
-       task = g_task_new (request, cancellable, callback, user_data);
-       g_task_set_task_data (task, td, thread_data_free);
-       g_task_run_in_thread (task, content_request_process_thread);
-       g_object_unref (task);
+       result = e_simple_async_result_new (G_OBJECT (request), callback, user_data, 
e_content_request_process);
+
+       e_simple_async_result_set_user_data (result, td, thread_data_free);
+       e_simple_async_result_run_in_thread (result, is_http ? G_PRIORITY_LOW : G_PRIORITY_DEFAULT, 
content_request_process_thread, cancellable);
+
+       g_object_unref (result);
 }
 
 gboolean
@@ -162,18 +169,24 @@ e_content_request_process_finish (EContentRequest *request,
 {
        ThreadData *td;
 
-       g_return_val_if_fail (g_task_is_valid (result, request), FALSE);
+       g_return_val_if_fail (g_async_result_is_tagged (result, e_content_request_process), FALSE);
        g_return_val_if_fail (E_IS_CONTENT_REQUEST (request), FALSE);
-       g_return_val_if_fail (G_IS_TASK (result), FALSE);
+       g_return_val_if_fail (E_IS_SIMPLE_ASYNC_RESULT (result), FALSE);
        g_return_val_if_fail (out_stream != NULL, FALSE);
        g_return_val_if_fail (out_stream_length != NULL, FALSE);
        g_return_val_if_fail (out_mime_type != NULL, FALSE);
 
-       td = g_task_get_task_data (G_TASK (result));
+       td = e_simple_async_result_get_user_data (E_SIMPLE_ASYNC_RESULT (result));
        g_return_val_if_fail (td != NULL, FALSE);
 
-       if (!g_task_propagate_boolean (G_TASK (result), error))
+       if (td->error || !td->success) {
+               if (td->error) {
+                       g_propagate_error (error, td->error);
+                       td->error = NULL;
+               }
+
                return FALSE;
+       }
 
        *out_stream = td->out_stream;
        *out_stream_length = td->out_stream_length;
diff --git a/src/e-util/e-simple-async-result.c b/src/e-util/e-simple-async-result.c
index bb9e87f..5959a69 100644
--- a/src/e-util/e-simple-async-result.c
+++ b/src/e-util/e-simple-async-result.c
@@ -206,6 +206,87 @@ e_simple_async_result_get_op_pointer (ESimpleAsyncResult *result)
        return result->priv->op_pointer;
 }
 
+static GThreadPool *thread_pool = NULL;
+static GThreadPool *low_prio_thread_pool = NULL;
+G_LOCK_DEFINE_STATIC (thread_pool);
+
+typedef struct _ThreadData {
+       ESimpleAsyncResult *result;
+       gint io_priority;
+       ESimpleAsyncResultThreadFunc func;
+       GCancellable *cancellable;
+} ThreadData;
+
+static gint
+e_simple_async_result_thread_pool_sort_func (gconstpointer ptra,
+                                            gconstpointer ptrb,
+                                            gpointer user_data)
+{
+       const ThreadData *tda = ptra, *tdb = ptrb;
+
+       if (!tda || !tdb)
+               return 0;
+
+       return tda->io_priority < tdb->io_priority ? -1 :
+              tda->io_priority > tdb->io_priority ? 1 : 0;
+}
+
+static void
+e_simple_async_result_thread (gpointer data,
+                             gpointer user_data)
+{
+       ThreadData *td = data;
+
+       g_return_if_fail (td != NULL);
+       g_return_if_fail (E_IS_SIMPLE_ASYNC_RESULT (td->result));
+       g_return_if_fail (td->func != NULL);
+
+       td->func (td->result,
+               g_async_result_get_source_object (G_ASYNC_RESULT (td->result)),
+               td->cancellable);
+
+       e_simple_async_result_complete_idle (td->result);
+
+       g_clear_object (&td->result);
+       g_clear_object (&td->cancellable);
+       g_free (td);
+}
+
+void
+e_simple_async_result_run_in_thread (ESimpleAsyncResult *result,
+                                    gint io_priority,
+                                    ESimpleAsyncResultThreadFunc func,
+                                    GCancellable *cancellable)
+{
+       ThreadData *td;
+
+       g_return_if_fail (E_IS_SIMPLE_ASYNC_RESULT (result));
+       g_return_if_fail (func != NULL);
+
+       td = g_new0 (ThreadData, 1);
+       td->result = g_object_ref (result);
+       td->io_priority = io_priority;
+       td->func = func;
+       td->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
+
+       G_LOCK (thread_pool);
+
+       if (!thread_pool) {
+               thread_pool = g_thread_pool_new (e_simple_async_result_thread, NULL, 10, FALSE, NULL);
+               g_thread_pool_set_sort_function (thread_pool, e_simple_async_result_thread_pool_sort_func, 
NULL);
+
+               low_prio_thread_pool = g_thread_pool_new (e_simple_async_result_thread, NULL, 10, FALSE, 
NULL);
+               g_thread_pool_set_sort_function (low_prio_thread_pool, 
e_simple_async_result_thread_pool_sort_func, NULL);
+       }
+
+       if (io_priority >= G_PRIORITY_LOW)
+               g_thread_pool_push (low_prio_thread_pool, td, NULL);
+       else
+               g_thread_pool_push (thread_pool, td, NULL);
+
+       G_UNLOCK (thread_pool);
+}
+
 void
 e_simple_async_result_complete (ESimpleAsyncResult *result)
 {
@@ -238,3 +319,21 @@ e_simple_async_result_complete_idle (ESimpleAsyncResult *result)
 
        g_idle_add (result_complete_idle_cb, g_object_ref (result));
 }
+
+void
+e_simple_async_result_free_global_memory (void)
+{
+       G_LOCK (thread_pool);
+
+       if (thread_pool) {
+               g_thread_pool_free (thread_pool, TRUE, FALSE);
+               thread_pool = NULL;
+       }
+
+       if (low_prio_thread_pool) {
+               g_thread_pool_free (low_prio_thread_pool, TRUE, FALSE);
+               low_prio_thread_pool = NULL;
+       }
+
+       G_UNLOCK (thread_pool);
+}
diff --git a/src/e-util/e-simple-async-result.h b/src/e-util/e-simple-async-result.h
index d2a5699..8319e96 100644
--- a/src/e-util/e-simple-async-result.h
+++ b/src/e-util/e-simple-async-result.h
@@ -48,6 +48,10 @@ typedef struct _ESimpleAsyncResult ESimpleAsyncResult;
 typedef struct _ESimpleAsyncResultClass ESimpleAsyncResultClass;
 typedef struct _ESimpleAsyncResultPrivate ESimpleAsyncResultPrivate;
 
+typedef void (* ESimpleAsyncResultThreadFunc)  (ESimpleAsyncResult *result,
+                                                gpointer source_object,
+                                                GCancellable *cancellable);
+
 /**
  * ESimpleAsyncResult:
  *
@@ -83,10 +87,16 @@ void                e_simple_async_result_set_op_pointer
                                                 gpointer ptr);
 gpointer       e_simple_async_result_get_op_pointer
                                                (ESimpleAsyncResult *result);
+void           e_simple_async_result_run_in_thread
+                                               (ESimpleAsyncResult *result,
+                                                gint io_priority,
+                                                ESimpleAsyncResultThreadFunc func,
+                                                GCancellable *cancellable);
 void           e_simple_async_result_complete  (ESimpleAsyncResult *result);
 void           e_simple_async_result_complete_idle
                                                (ESimpleAsyncResult *result);
-
+void           e_simple_async_result_free_global_memory
+                                               (void);
 
 G_END_DECLS
 
diff --git a/src/e-util/e-web-view.c b/src/e-util/e-web-view.c
index f13d14c..8b1500a 100644
--- a/src/e-util/e-web-view.c
+++ b/src/e-util/e-web-view.c
@@ -99,6 +99,8 @@ struct _EWebViewPrivate {
 
        gboolean need_input;
        guint web_extension_need_input_changed_signal_id;
+
+       GCancellable *load_cancellable;
 };
 
 struct _AsyncContext {
@@ -962,6 +964,11 @@ web_view_dispose (GObject *object)
 
        priv = E_WEB_VIEW_GET_PRIVATE (object);
 
+       if (priv->load_cancellable) {
+               g_cancellable_cancel (priv->load_cancellable);
+               g_clear_object (&priv->load_cancellable);
+       }
+
        if (priv->font_name_changed_handler_id > 0) {
                g_signal_handler_disconnect (
                        priv->font_settings,
@@ -1097,6 +1104,7 @@ static void
 web_view_process_uri_request_cb (WebKitURISchemeRequest *request,
                                 gpointer user_data)
 {
+       EWebView *web_view = NULL;
        EContentRequest *content_request = user_data;
        const gchar *uri;
        gchar *redirect_to_uri = NULL;
@@ -1140,9 +1148,11 @@ web_view_process_uri_request_cb (WebKitURISchemeRequest *request,
 
                        return;
                }
+
+               web_view = E_WEB_VIEW (requester);
        }
 
-       e_content_request_process (content_request, uri, requester, NULL,
+       e_content_request_process (content_request, uri, requester, web_view ? 
web_view->priv->load_cancellable : NULL,
                web_view_uri_request_done_cb, g_object_ref (request));
 
        g_free (redirect_to_uri);
@@ -1255,6 +1265,21 @@ web_view_constructed (GObject *object)
        web_view_set_find_controller (E_WEB_VIEW (object));
 }
 
+static void
+e_web_view_replace_load_cancellable (EWebView *web_view,
+                                    gboolean create_new)
+{
+       g_return_if_fail (E_IS_WEB_VIEW (web_view));
+
+       if (web_view->priv->load_cancellable) {
+               g_cancellable_cancel (web_view->priv->load_cancellable);
+               g_clear_object (&web_view->priv->load_cancellable);
+       }
+
+       if (create_new)
+               web_view->priv->load_cancellable = g_cancellable_new ();
+}
+
 static gboolean
 web_view_scroll_event (GtkWidget *widget,
                        GdkEventScroll *event)
@@ -1473,6 +1498,7 @@ web_view_popup_event (EWebView *web_view,
 static void
 web_view_stop_loading (EWebView *web_view)
 {
+       e_web_view_replace_load_cancellable (web_view, FALSE);
        webkit_web_view_stop_loading (WEBKIT_WEB_VIEW (web_view));
 }
 
@@ -2528,6 +2554,8 @@ e_web_view_init (EWebView *web_view)
        e_plugin_ui_enable_manager (ui_manager, id);
 
        web_view->priv->element_clicked_cbs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, 
(GDestroyNotify) g_ptr_array_unref);
+
+       web_view->priv->load_cancellable = NULL;
 }
 
 GtkWidget *
@@ -2543,6 +2571,8 @@ e_web_view_clear (EWebView *web_view)
 {
        g_return_if_fail (E_IS_WEB_VIEW (web_view));
 
+       e_web_view_replace_load_cancellable (web_view, FALSE);
+
        webkit_web_view_load_html (
                WEBKIT_WEB_VIEW (web_view),
                "<html>"
@@ -2563,6 +2593,8 @@ e_web_view_load_string (EWebView *web_view,
        class = E_WEB_VIEW_GET_CLASS (web_view);
        g_return_if_fail (class->load_string != NULL);
 
+       e_web_view_replace_load_cancellable (web_view, TRUE);
+
        class->load_string (web_view, string);
 }
 
@@ -2577,6 +2609,8 @@ e_web_view_load_uri (EWebView *web_view,
        class = E_WEB_VIEW_GET_CLASS (web_view);
        g_return_if_fail (class->load_uri != NULL);
 
+       e_web_view_replace_load_cancellable (web_view, TRUE);
+
        class->load_uri (web_view, uri);
 }
 
@@ -2626,6 +2660,8 @@ e_web_view_reload (EWebView *web_view)
 {
        g_return_if_fail (E_IS_WEB_VIEW (web_view));
 
+       e_web_view_replace_load_cancellable (web_view, TRUE);
+
        webkit_web_view_reload (WEBKIT_WEB_VIEW (web_view));
 }
 
diff --git a/src/modules/webkit-editor/e-webkit-editor.c b/src/modules/webkit-editor/e-webkit-editor.c
index c923a9c..511503e 100644
--- a/src/modules/webkit-editor/e-webkit-editor.c
+++ b/src/modules/webkit-editor/e-webkit-editor.c
@@ -5092,6 +5092,7 @@ static void
 webkit_editor_process_uri_request_cb (WebKitURISchemeRequest *request,
                                      gpointer user_data)
 {
+       EWebKitEditor *wk_editor;
        EContentRequest *content_request = user_data;
        const gchar *uri;
        GObject *requester;
@@ -5114,7 +5115,9 @@ webkit_editor_process_uri_request_cb (WebKitURISchemeRequest *request,
 
        g_return_if_fail (e_content_request_can_process_uri (content_request, uri));
 
-       e_content_request_process (content_request, uri, requester, NULL,
+       wk_editor = E_IS_WEBKIT_EDITOR (requester) ? E_WEBKIT_EDITOR (requester) : NULL;
+
+       e_content_request_process (content_request, uri, requester, wk_editor ? wk_editor->priv->cancellable 
: NULL,
                webkit_editor_uri_request_done_cb, g_object_ref (request));
 }
 
diff --git a/src/shell/main.c b/src/shell/main.c
index 17fd8a9..2863d35 100644
--- a/src/shell/main.c
+++ b/src/shell/main.c
@@ -689,6 +689,7 @@ exit:
 
        e_util_cleanup_settings ();
        e_spell_checker_free_global_memory ();
+       e_simple_async_result_free_global_memory ();
 
        return 0;
 }


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