+ {
+ g_task_return_error (task, error);
+ return;
+ }
+ }
+
+ g_task_return_boolean (task, TRUE);
+}
+
+static void
+g_io_stream_real_close_async (GIOStream *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_check_cancellable (task, FALSE);
+ g_task_set_priority (task, io_priority);
+
+ g_task_run_in_thread (task, close_async_thread);
+ g_object_unref (task);
+}
+
+static gboolean
+g_io_stream_real_close_finish (GIOStream *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);
+}
+
+typedef struct
+{
+ GIOStream *stream1;
+ GIOStream *stream2;
+ GIOStreamSpliceFlags flags;
+ gint io_priority;
+ GCancellable *cancellable;
+ gulong cancelled_id;
+ GCancellable *op1_cancellable;
+ GCancellable *op2_cancellable;
+ guint completed;
+ GError *error;
+} SpliceContext;
+
+static void
+splice_context_free (SpliceContext *ctx)
+{
+ g_object_unref (ctx->stream1);
+ g_object_unref (ctx->stream2);
+ if (ctx->cancellable != NULL)
+ g_object_unref (ctx->cancellable);
+ g_object_unref (ctx->op1_cancellable);
+ g_object_unref (ctx->op2_cancellable);
+ g_clear_error (&ctx->error);
+ g_slice_free (SpliceContext, ctx);
+}
+
+static void
+splice_complete (GTask *task,
+ SpliceContext *ctx)
+{
+ if (ctx->cancelled_id != 0)
+ g_cancellable_disconnect (ctx->cancellable, ctx->cancelled_id);
+ ctx->cancelled_id = 0;
+
+ if (ctx->error != NULL)
+ {
+ g_task_return_error (task, ctx->error);
+ ctx->error = NULL;
+ }
+ else
+ g_task_return_boolean (task, TRUE);
+}
+
+static void
+splice_close_cb (GObject *iostream,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GTask *task = user_data;
+ SpliceContext *ctx = g_task_get_task_data (task);
+ GError *error = NULL;
+
+ g_io_stream_close_finish (G_IO_STREAM (iostream), res, &error);
+
+ ctx->completed++;
+
+ /* Keep the first error that occurred */
+ if (error != NULL && ctx->error == NULL)
+ ctx->error = error;
+ else
+ g_clear_error (&error);
+
+ /* If all operations are done, complete now */
+ if (ctx->completed == 4)
+ splice_complete (task, ctx);
+
+ g_object_unref (task);
+}
+
+static void
+splice_cb (GObject *ostream,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GTask *task = user_data;
+ SpliceContext *ctx = g_task_get_task_data (task);
+ GError *error = NULL;
+
+ g_output_stream_splice_finish (G_OUTPUT_STREAM (ostream), res, &error);
+
+ ctx->completed++;
+
+ /* ignore cancellation error if it was not requested by the user */
+ if (error != NULL &&
+ g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) &&
+ (ctx->cancellable == NULL ||
+ !g_cancellable_is_cancelled (ctx->cancellable)))
+ g_clear_error (&error);
+
+ /* Keep the first error that occurred */
+ if (error != NULL && ctx->error == NULL)
+ ctx->error = error;
+ else
+ g_clear_error (&error);
+
+ if (ctx->completed == 1 &&
+ (ctx->flags & G_IO_STREAM_SPLICE_WAIT_FOR_BOTH) == 0)
+ {
+ /* We don't want to wait for the 2nd operation to finish, cancel it */
+ g_cancellable_cancel (ctx->op1_cancellable);
+ g_cancellable_cancel (ctx->op2_cancellable);
+ }
+ else if (ctx->completed == 2)
+ {
+ if (ctx->cancellable == NULL ||
+ !g_cancellable_is_cancelled (ctx->cancellable))
+ {
+ g_cancellable_reset (ctx->op1_cancellable);
+ g_cancellable_reset (ctx->op2_cancellable);
+ }
+
+ /* Close the IO streams if needed */
+ if ((ctx->flags & G_IO_STREAM_SPLICE_CLOSE_STREAM1) != 0)