mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-12-25 15:06:14 +01:00
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:
parent
cb40c553ae
commit
c2c0a6ae5c
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user