From aff71954924b8e428f5bc54c0072bae1e5a5afde Mon Sep 17 00:00:00 2001 From: Milan Crha Date: Tue, 3 Oct 2017 11:43:19 +0200 Subject: [PATCH] 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 3e95094af7..9be6e09ad8 100644 --- a/src/e-util/e-content-request.c +++ b/src/e-util/e-content-request.c @@ -20,7 +20,8 @@ #include #include -#include + +#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 bb9e87f92c..5959a699b6 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 d2a5699e8c..8319e96894 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 f13d14c292..8b1500a569 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), "" @@ -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 c923a9c271..511503ef32 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 17fd8a98da..2863d3512e 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; } -- 2.11.4.GIT