[glib/wip/carlosg/cancelled-splice: 1/2] goutputstream: Check individual close operations after splice




commit c033450f9369a50c91fa957da280c02c050093d6
Author: Carlos Garnacho <carlosg gnome org>
Date:   Mon Aug 24 12:00:44 2020 +0200

    goutputstream: Check individual close operations after splice
    
    After a splice operation is finished, it attempts to 1) close input/output
    streams, as per the given flags, and 2) return the operation result (maybe
    an error, too).
    
    However, if the operation gets cancelled early and the streams indirectly
    closed, the splice operation will try to close both descriptors and return
    on the task when both are already closed. The catch here is that getting the
    streams closed under its feet is possible, so the completion callback would
    find both streams closed after returning on the first close operation and
    return the error, but then the second operation could be able to trigger
    a second error which would be returned as well.
    
    What happens here is up to further race conditions, if the task didn't
    return yet, the returned error will be simply replaced (but the old one not
    freed...), if it did already return, it'll result in:
    
    GLib-GIO-FATAL-CRITICAL: g_task_return_error: assertion '!task->ever_returned' failed
    
    Fix this by flagging the close_async() callbacks, and checking that both
    close operations did return, instead of checking that both streams are
    closed by who knows.
    
    This error triggers a semi-frequent CI failure in tracker, see the summary at
    https://gitlab.gnome.org/GNOME/tracker/-/issues/240

 gio/goutputstream.c | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)
---
diff --git a/gio/goutputstream.c b/gio/goutputstream.c
index 87b61a4ec..9e2848e37 100644
--- a/gio/goutputstream.c
+++ b/gio/goutputstream.c
@@ -2643,6 +2643,8 @@ g_output_stream_real_writev_finish (GOutputStream   *stream,
 typedef struct {
   GInputStream *source;
   GOutputStreamSpliceFlags flags;
+  guint istream_closed : 1;
+  guint ostream_closed : 1;
   gssize n_read;
   gssize n_written;
   gsize bytes_copied;
@@ -2665,11 +2667,11 @@ real_splice_async_complete_cb (GTask *task)
   SpliceData *op = g_task_get_task_data (task);
 
   if (op->flags & G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE &&
-      !g_input_stream_is_closed (op->source))
+      !op->istream_closed)
     return;
 
   if (op->flags & G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET &&
-      !g_output_stream_is_closed (g_task_get_source_object (task)))
+      !op->ostream_closed)
     return;
 
   if (op->error != NULL)
@@ -2691,8 +2693,10 @@ real_splice_async_close_input_cb (GObject      *source,
                                   gpointer      user_data)
 {
   GTask *task = user_data;
+  SpliceData *op = g_task_get_task_data (task);
 
   g_input_stream_close_finish (G_INPUT_STREAM (source), res, NULL);
+  op->istream_closed = TRUE;
 
   real_splice_async_complete_cb (task);
 }
@@ -2707,6 +2711,7 @@ real_splice_async_close_output_cb (GObject      *source,
   GError **error = (op->error == NULL) ? &op->error : NULL;
 
   g_output_stream_internal_close_finish (G_OUTPUT_STREAM (source), res, error);
+  op->ostream_closed = TRUE;
 
   real_splice_async_complete_cb (task);
 }


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]