[libgdata] core: Return G_IO_ERROR_CLOSED if stream close methods are called again



commit 063845eaabfdf0703914c3d59b63a7945250ce4d
Author: Philip Withnall <philip tecnocode co uk>
Date:   Mon Dec 20 10:09:21 2010 +0000

    core: Return G_IO_ERROR_CLOSED if stream close methods are called again
    
    While g_[input|output]_stream_close() prevents the close method implementation
    from being called multiple times, other methods in GIO (such as
    g_output_stream_splice()) call the close method implementation directly, so
    gdata_[upload|download]_stream_close() need to be safe to call multiple
    times.

 gdata/gdata-download-stream.c |   16 +++++++++++++---
 gdata/gdata-upload-stream.c   |   16 +++++++++++++---
 2 files changed, 26 insertions(+), 6 deletions(-)
---
diff --git a/gdata/gdata-download-stream.c b/gdata/gdata-download-stream.c
index ce11a5f..493eefe 100644
--- a/gdata/gdata-download-stream.c
+++ b/gdata/gdata-download-stream.c
@@ -39,6 +39,7 @@
 
 #include <config.h>
 #include <glib.h>
+#include <glib/gi18n-lib.h>
 
 #include "gdata-download-stream.h"
 #include "gdata-buffer.h"
@@ -476,9 +477,9 @@ close_cancelled_cb (GCancellable *cancellable, CancelledData *data)
 	g_static_mutex_unlock (&(priv->finished_mutex));
 }
 
-/* If g_input_stream_close() has already been called once on this stream, g_input_stream_is_closed() is set to %TRUE, and all future calls to
- * g_input_stream_close() will immediately return, i.e. gdata_download_stream_close() is guaranteed to only ever run once (even if the first call was
- * cancelled or returned an error).
+/* Even though calling g_input_stream_close() multiple times on this stream is guaranteed to call gdata_download_stream_close() at most once, other
+ * GIO methods (notably g_output_stream_splice()) can call gdata_download_stream_close() directly. Consequently, we need to be careful to be idempotent
+ * after the first call.
  *
  * If the network thread hasn't yet been started (i.e. gdata_download_stream_read() hasn't been called at all yet), %TRUE will be returned immediately.
  *
@@ -503,6 +504,15 @@ gdata_download_stream_close (GInputStream *stream, GCancellable *cancellable, GE
 	if (priv->network_thread == NULL)
 		return TRUE;
 
+	/* If we've already closed the stream, return G_IO_ERROR_CLOSED */
+	g_static_mutex_lock (&(priv->finished_mutex));
+	if (priv->finished == FALSE) {
+		g_static_mutex_unlock (&(priv->finished_mutex));
+		g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_CLOSED, _("Stream is already closed"));
+		return FALSE;
+	}
+	g_static_mutex_unlock (&(priv->finished_mutex));
+
 	/* Allow cancellation */
 	data.download_stream = GDATA_DOWNLOAD_STREAM (stream);
 	data.cancelled = &cancelled;
diff --git a/gdata/gdata-upload-stream.c b/gdata/gdata-upload-stream.c
index 6405349..8f40c0b 100644
--- a/gdata/gdata-upload-stream.c
+++ b/gdata/gdata-upload-stream.c
@@ -59,6 +59,7 @@
 
 #include <config.h>
 #include <glib.h>
+#include <glib/gi18n-lib.h>
 #include <string.h>
 
 #include "gdata-upload-stream.h"
@@ -614,9 +615,9 @@ close_cancelled_cb (GCancellable *cancellable, CancelledData *data)
 /* It's guaranteed that we have set ->response_status and ->response_error and are done with *all* network activity before this returns, unless it's
  * cancelled. This means that it's safe to call gdata_upload_stream_get_response() once a call to close() has returned without being cancelled.
  *
- * If g_output_stream_close() has already been called once on this stream, g_output_stream_is_closed() is set to %TRUE, and all future calls to
- * g_output_stream_close() will immediately return, i.e. gdata_upload_stream_close() is guaranteed to only ever run once (even if the first call was
- * cancelled or returned an error).
+ * Even though calling g_output_stream_close() multiple times on this stream is guaranteed to call gdata_upload_stream_close() at most once, other
+ * GIO methods (notably g_output_stream_splice()) can call gdata_upload_stream_close() directly. Consequently, we need to be careful to be idempotent
+ * after the first call.
  *
  * If the network thread hasn't yet been started (i.e. gdata_upload_stream_write() hasn't been called at all yet), %TRUE will be returned immediately.
  *
@@ -642,6 +643,15 @@ gdata_upload_stream_close (GOutputStream *stream, GCancellable *cancellable, GEr
 	if (priv->network_thread == NULL)
 		return TRUE;
 
+	/* If we've already closed the stream, return G_IO_ERROR_CLOSED */
+	g_static_mutex_lock (&(priv->response_mutex));
+	if (priv->response_status != SOUP_STATUS_NONE) {
+		g_static_mutex_unlock (&(priv->response_mutex));
+		g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_CLOSED, _("Stream is already closed"));
+		return FALSE;
+	}
+	g_static_mutex_unlock (&(priv->response_mutex));
+
 	/* Allow cancellation */
 	data.upload_stream = GDATA_UPLOAD_STREAM (stream);
 	data.cancelled = &cancelled;



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