/pidgin/main: 266009ac8af6: purplequeuedoutputstream: Implement ...
Mike Ruprecht <[email protected]> Tue, 06 Sep 2016 11:19:22 -0400
| Newsgroups | gmane.comp.gnome.gaim.cvs |
|---|---|
| Message-ID | <[email protected]> |
Changeset: 266009ac8af6c26f70040bde47c9318461315c54 Author: Mike Ruprecht <[email protected]> Date: 2016-08-30 03:01 -0500 Branch: default URL: https://hg.pidgin.im/pidgin/main/rev/266009ac8af6 Description: purplequeuedoutputstream: Implement unthreaded asynchronous flush This patch implements an unthreaded asynchronous flush implementation for PurpleQueuedOutputStream. This fixes a potential race condition with the default, threaded flush implementation, the way it's currently used. Prior to this patch, the PurpleQueuedOutputStream could finish flushing in a thread, more data could be queued while the flushing operation is pending, then the flushing operation could finish. This would otherwise cause the queued data to sit there, idle, without the consumer realizing there's further data to send. The asynchronous flush implementation solves this by flushing in the default context and, provided there is data to flush, returns without the possibility for such a race condition. diffstat: libpurple/queuedoutputstream.c | 92 ++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 92 insertions(+), 0 deletions(-) diffs (113 lines): diff --git a/libpurple/queuedoutputstream.c b/libpurple/queuedoutputstream.c --- a/libpurple/queuedoutputstream.c +++ b/libpurple/queuedoutputstream.c @@ -43,6 +43,13 @@ G_DEFINE_TYPE_WITH_CODE(PurpleQueuedOutp static void purple_queued_output_stream_dispose(GObject *object); static gboolean purple_queued_output_stream_flush(GOutputStream *stream, GCancellable *cancellable, GError **error); +static void purple_queued_output_stream_flush_async(GOutputStream *stream, + int io_priority, GCancellable *cancellable, + GAsyncReadyCallback callback, gpointer user_data); +static gboolean purple_queued_output_stream_flush_finish(GOutputStream *stream, + GAsyncResult *result, GError **error); + +static void purple_queued_output_stream_start_flush_async(GTask *task); static void purple_queued_output_stream_class_init(PurpleQueuedOutputStreamClass *klass) @@ -57,6 +64,8 @@ purple_queued_output_stream_class_init(P ostream_class = G_OUTPUT_STREAM_CLASS(klass); ostream_class->flush = purple_queued_output_stream_flush; + ostream_class->flush_async = purple_queued_output_stream_flush_async; + ostream_class->flush_finish = purple_queued_output_stream_flush_finish; } PurpleQueuedOutputStream * @@ -156,3 +165,86 @@ purple_queued_output_stream_flush(GOutpu return ret; } +static void +purple_queued_output_stream_flush_async_cb(GObject *source, + GAsyncResult *res, gpointer user_data) +{ + GTask *task = user_data; + PurpleQueuedOutputStream *stream; + gssize written; + gsize size; + GBytes *old_bytes; + GError *error = NULL; + + written = g_output_stream_write_bytes_finish(G_OUTPUT_STREAM(source), + res, &error); + + if (written < 0) { + g_task_return_error(task, error); + return; + } + + stream = PURPLE_QUEUED_OUTPUT_STREAM(g_task_get_source_object(task)); + size = g_bytes_get_size(stream->priv->next); + + old_bytes = stream->priv->next; + stream->priv->next = NULL; + + if (size > (gsize)written) { + stream->priv->next = g_bytes_new_from_bytes(old_bytes, + written, size - written); + } + + g_bytes_unref(old_bytes); + + purple_queued_output_stream_start_flush_async(task); +} + +static void +purple_queued_output_stream_start_flush_async(GTask *task) +{ + PurpleQueuedOutputStream *stream; + GOutputStream *base_stream; + + stream = PURPLE_QUEUED_OUTPUT_STREAM(g_task_get_source_object(task)); + base_stream = g_filter_output_stream_get_base_stream( + G_FILTER_OUTPUT_STREAM(stream)); + + if (stream->priv->next == NULL) { + stream->priv->next = + g_async_queue_try_pop(stream->priv->queue); + + if (stream->priv->next == NULL) { + g_task_return_boolean(task, TRUE); + return; + } + } + + g_output_stream_write_bytes_async(base_stream, stream->priv->next, + g_task_get_priority(task), + g_task_get_cancellable(task), + purple_queued_output_stream_flush_async_cb, task); +} + +static void +purple_queued_output_stream_flush_async(GOutputStream *stream, + int io_priority, GCancellable *cancellable, + GAsyncReadyCallback callback, gpointer user_data) +{ + GTask *task; + + task = g_task_new(stream, cancellable, callback, user_data); + g_task_set_priority(task, io_priority); + + purple_queued_output_stream_start_flush_async(task); +} + +static gboolean +purple_queued_output_stream_flush_finish(GOutputStream *stream, + GAsyncResult *result, GError **error) +{ + g_return_val_if_fail(g_task_is_valid(result, stream), FALSE); + + return g_task_propagate_boolean(G_TASK(result), error); +} + _______________________________________________ Commits mailing list [email protected] https://pidgin.im/cgi-bin/mailman/listinfo/commits