GIOStream: support for unemulated async close()

Add an implementation of non-thread-emulated async close of a GIOStream
if either of the underlying stream objects support it.

This prevents us from calling close() functions from another thread on
an object that may not be expecting that.  It also allows us to skip the
thread entirely in case our objects support a pure async close.

https://bugzilla.gnome.org/show_bug.cgi?id=741630
This commit is contained in:
Ryan Lortie 2015-01-20 08:11:02 -05:00
parent cb40c553ae
commit c2c0a6ae5c

View File

@ -26,6 +26,7 @@
#include "giostream.h"
#include "gasyncresult.h"
#include "gioprivate.h"
#include "gtask.h"
/**
@ -542,6 +543,53 @@ close_async_thread (GTask *task,
g_task_return_boolean (task, TRUE);
}
typedef struct
{
GError *error;
gint pending;
} CloseAsyncData;
static void
stream_close_complete (GObject *source,
GAsyncResult *result,
gpointer user_data)
{
GTask *task = user_data;
CloseAsyncData *data;
data = g_task_get_task_data (task);
data->pending--;
if (G_IS_OUTPUT_STREAM (source))
{
GError *error = NULL;
/* Match behaviour with the sync route and give precedent to the
* error returned from closing the output stream.
*/
g_output_stream_close_finish (G_OUTPUT_STREAM (source), result, &error);
if (error)
{
if (data->error)
g_error_free (data->error);
data->error = error;
}
}
else
g_input_stream_close_finish (G_INPUT_STREAM (source), result, data->error ? NULL : &data->error);
if (data->pending == 0)
{
if (data->error)
g_task_return_error (task, data->error);
else
g_task_return_boolean (task, TRUE);
g_slice_free (CloseAsyncData, data);
g_object_unref (task);
}
}
static void
g_io_stream_real_close_async (GIOStream *stream,
int io_priority,
@ -549,14 +597,41 @@ g_io_stream_real_close_async (GIOStream *stream,
GAsyncReadyCallback callback,
gpointer user_data)
{
GInputStream *input;
GOutputStream *output;
GTask *task;
task = g_task_new (stream, cancellable, callback, user_data);
g_task_set_check_cancellable (task, FALSE);
g_task_set_priority (task, io_priority);
g_task_run_in_thread (task, close_async_thread);
g_object_unref (task);
input = g_io_stream_get_input_stream (stream);
output = g_io_stream_get_output_stream (stream);
if (g_input_stream_async_close_is_via_threads (input) && g_output_stream_async_close_is_via_threads (output))
{
/* No sense in dispatching to the thread twice -- just do it all
* in one go.
*/
g_task_run_in_thread (task, close_async_thread);
g_object_unref (task);
}
else
{
CloseAsyncData *data;
/* We should avoid dispatching to another thread in case either
* object that would not do it for itself because it may not be
* threadsafe.
*/
data = g_slice_new (CloseAsyncData);
data->error = NULL;
data->pending = 2;
g_task_set_task_data (task, data, NULL);
g_input_stream_close_async (input, io_priority, cancellable, stream_close_complete, task);
g_output_stream_close_async (output, io_priority, cancellable, stream_close_complete, task);
}
}
static gboolean