[evolution] Bug 724909 - Highlight module hangs on large attachments
- From: Matthew Barnes <mbarnes src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution] Bug 724909 - Highlight module hangs on large attachments
- Date: Tue, 25 Feb 2014 03:56:12 +0000 (UTC)
commit e15c81b8ee34fff9aa1da9085587e568e59da854
Author: Matthew Barnes <mbarnes redhat com>
Date: Mon Feb 24 22:28:04 2014 -0500
Bug 724909 - Highlight module hangs on large attachments
The previous code was writing the entire MIME part content to the
highlight utility's stdin pipe before reading the converted result.
With enough content, this caused the write operation to get stuck.
What's worse is this all happens synchronously in the UI thread.
Not sure exactly what was going on, but my hunch proved correct that
we need to simultaneously write to the stdin pipe and read from the
stdout pipe to avoid the deadlock.
Still not happy about this blocking the UI, but that would require
some major refactoring in libevolution-mail-formatter.
.../e-mail-formatter-text-highlight.c | 209 +++++++++++++++++---
1 files changed, 182 insertions(+), 27 deletions(-)
---
diff --git a/modules/text-highlight/e-mail-formatter-text-highlight.c
b/modules/text-highlight/e-mail-formatter-text-highlight.c
index d43c01e..cbf56ad 100644
--- a/modules/text-highlight/e-mail-formatter-text-highlight.c
+++ b/modules/text-highlight/e-mail-formatter-text-highlight.c
@@ -22,6 +22,10 @@
#include "e-mail-formatter-text-highlight.h"
#include "languages.h"
+/* FIXME Delete these once we can use GSubprocess. */
+#include <gio/gunixinputstream.h>
+#include <gio/gunixoutputstream.h>
+
#include <em-format/e-mail-formatter-extension.h>
#include <em-format/e-mail-formatter.h>
#include <em-format/e-mail-part-utils.h>
@@ -40,6 +44,14 @@ typedef EMailFormatterExtensionClass EMailFormatterTextHighlightClass;
typedef EExtension EMailFormatterTextHighlightLoader;
typedef EExtensionClass EMailFormatterTextHighlightLoaderClass;
+typedef struct _TextHighlightClosure TextHighlightClosure;
+
+struct _TextHighlightClosure {
+ GMainLoop *main_loop;
+ GError *input_error;
+ GError *output_error;
+};
+
GType e_mail_formatter_text_highlight_get_type (void);
G_DEFINE_DYNAMIC_TYPE (
@@ -109,6 +121,155 @@ get_syntax (EMailPart *part,
return syntax;
}
+static void
+text_highlight_input_spliced (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ TextHighlightClosure *closure = user_data;
+
+ g_output_stream_splice_finish (
+ G_OUTPUT_STREAM (source_object),
+ result, &closure->input_error);
+}
+
+static void
+text_highlight_output_spliced (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ TextHighlightClosure *closure = user_data;
+
+ g_output_stream_splice_finish (
+ G_OUTPUT_STREAM (source_object),
+ result, &closure->output_error);
+
+ g_main_loop_quit (closure->main_loop);
+}
+
+static gboolean
+text_highlight_feed_data (CamelStream *stream,
+ CamelDataWrapper *data_wrapper,
+ gint pipe_stdin,
+ gint pipe_stdout,
+ GCancellable *cancellable,
+ GError **error)
+{
+ TextHighlightClosure closure;
+ GInputStream *input_stream = NULL;
+ GOutputStream *output_stream = NULL;
+ GInputStream *stdout_stream = NULL;
+ GOutputStream *stdin_stream = NULL;
+ GMainContext *main_context;
+ gchar *utf8_data;
+ gconstpointer data;
+ gssize bytes_written;
+ gsize size;
+ gboolean success;
+
+ /* We need to dump CamelDataWrapper to a buffer, force the content
+ * to valid UTF-8, feed the UTF-8 data to the 'highlight' process,
+ * read the converted data back and feed it to the CamelStream. */
+
+ /* FIXME Use GSubprocess once we can require GLib 2.40. */
+
+ output_stream = g_memory_output_stream_new_resizable ();
+
+ success = camel_data_wrapper_decode_to_output_stream_sync (
+ data_wrapper, output_stream, cancellable, error);
+
+ if (!success)
+ goto exit;
+
+ main_context = g_main_context_new ();
+
+ closure.main_loop = g_main_loop_new (main_context, FALSE);
+ closure.input_error = NULL;
+ closure.output_error = NULL;
+
+ g_main_context_push_thread_default (main_context);
+
+ data = g_memory_output_stream_get_data (
+ G_MEMORY_OUTPUT_STREAM (output_stream));
+ size = g_memory_output_stream_get_data_size (
+ G_MEMORY_OUTPUT_STREAM (output_stream));
+
+ /* FIXME Write a GConverter that does this so we can decode
+ * straight to the stdin pipe and skip all this extra
+ * buffering. */
+ utf8_data = e_util_utf8_data_make_valid ((gchar *) data, size);
+
+ g_clear_object (&output_stream);
+
+ /* Takes ownership of the UTF-8 string. */
+ input_stream = g_memory_input_stream_new_from_data (
+ utf8_data, -1, (GDestroyNotify) g_free);
+
+ output_stream = g_memory_output_stream_new_resizable ();
+
+ stdin_stream = g_unix_output_stream_new (pipe_stdin, TRUE);
+ stdout_stream = g_unix_input_stream_new (pipe_stdout, TRUE);
+
+ /* Splice the streams together. */
+
+ /* GCancellable is only supposed to be used in one operation
+ * at a time. Skip it here and use it for reading converted
+ * data, since that operation terminates the main loop. */
+ g_output_stream_splice_async (
+ stdin_stream, input_stream,
+ G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE |
+ G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
+ G_PRIORITY_DEFAULT, NULL,
+ text_highlight_input_spliced,
+ &closure);
+
+ g_output_stream_splice_async (
+ output_stream, stdout_stream,
+ G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE |
+ G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
+ G_PRIORITY_DEFAULT, cancellable,
+ text_highlight_output_spliced,
+ &closure);
+
+ g_main_loop_run (closure.main_loop);
+
+ g_main_context_pop_thread_default (main_context);
+
+ g_main_context_unref (main_context);
+ g_main_loop_unref (closure.main_loop);
+
+ g_clear_object (&input_stream);
+ g_clear_object (&stdin_stream);
+ g_clear_object (&stdout_stream);
+
+ if (closure.input_error != NULL) {
+ g_propagate_error (error, closure.input_error);
+ g_clear_error (&closure.output_error);
+ success = FALSE;
+ goto exit;
+ }
+
+ if (closure.output_error != NULL) {
+ g_propagate_error (error, closure.output_error);
+ success = FALSE;
+ goto exit;
+ }
+
+ data = g_memory_output_stream_get_data (
+ G_MEMORY_OUTPUT_STREAM (output_stream));
+ size = g_memory_output_stream_get_data_size (
+ G_MEMORY_OUTPUT_STREAM (output_stream));
+
+ bytes_written = camel_stream_write (
+ stream, data, size, cancellable, error);
+ success = (bytes_written >= 0);
+
+exit:
+ g_clear_object (&output_stream);
+
+ return success;
+}
+
static gboolean
emfe_text_highlight_format (EMailFormatterExtension *extension,
EMailFormatter *formatter,
@@ -212,36 +373,30 @@ emfe_text_highlight_format (EMailFormatterExtension *extension,
&pid, &pipe_stdin, &pipe_stdout, NULL, NULL);
if (success) {
- CamelStream *read;
- CamelStream *write;
- CamelStream *utf8;
- GByteArray *ba;
- gchar *tmp;
-
- write = camel_stream_fs_new_with_fd (pipe_stdin);
- read = camel_stream_fs_new_with_fd (pipe_stdout);
-
- /* Decode the content of mime part to the 'utf8' stream */
- utf8 = camel_stream_mem_new ();
- camel_data_wrapper_decode_to_stream_sync (
- dw, utf8, cancellable, NULL);
-
- /* Convert the binary data do someting displayable */
- ba = camel_stream_mem_get_byte_array (CAMEL_STREAM_MEM (utf8));
- tmp = e_util_utf8_data_make_valid ((gchar *) ba->data, ba->len);
-
- /* Send the sanitized data to the highlighter */
- camel_stream_write_string (write, tmp, cancellable, NULL);
- g_free (tmp);
- g_object_unref (utf8);
- g_object_unref (write);
+ GError *local_error = NULL;
+
+ success = text_highlight_feed_data (
+ stream, dw,
+ pipe_stdin, pipe_stdout,
+ cancellable, &local_error);
+
+ if (g_error_matches (
+ local_error, G_IO_ERROR,
+ G_IO_ERROR_CANCELLED)) {
+ /* Do nothing. */
+
+ } else if (local_error != NULL) {
+ g_warning (
+ "%s: %s", G_STRFUNC,
+ local_error->message);
+ }
+
+ g_clear_error (&local_error);
g_spawn_close_pid (pid);
+ }
- g_seekable_seek (G_SEEKABLE (read), 0, G_SEEK_SET, cancellable, NULL);
- camel_stream_write_to_stream (read, stream, cancellable, NULL);
- g_object_unref (read);
- } else {
+ if (!success) {
/* We can't call e_mail_formatter_format_as on text/plain,
* because text-highlight is registered as an handler for
* text/plain, so we would end up in an endless recursion.
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]