Bug 568394 – dropping the last reference to a stream filter closes the
authorRyan Lortie <desrt@desrt.ca>
Wed, 21 Jan 2009 14:09:56 +0000 (14:09 +0000)
committerRyan Lortie <ryanl@src.gnome.org>
Wed, 21 Jan 2009 14:09:56 +0000 (14:09 +0000)
2009-01-20  Ryan Lortie  <desrt@desrt.ca>

        Bug 568394 – dropping the last reference to a stream filter closes the
        base stream

        * gfilterinputstream.h:
        * gfilterinputstream.c: add "close-base-stream" property and only
        close the base stream if it is true.  issue async close callbacks from
        correct source object.
        * gfilteroutputstream.h:
        * gfilteroutputstream.c: add a "close-base-stream" property and only
        close the base stream if it is true.  issue async close callbacks from
        correct source object.
        * gbufferedoutputstream: check g_filter_output_stream_get_close_base()
        before closing the base stream.  fix invalid source tag comparison in
        close_async (was comparing to flush_async).
        * ../docs/reference/gio/gio-sections.txt:
        * gio.symbols: add
        g_filter_{in,out}put_stream_{g,s}et_close_base_stream
        * tests/filter-streams.c: new test cases
        * tests/Makefile.am: add new test
        * tests/.gitignore: add new test

svn path=/trunk/; revision=7825

docs/reference/gio/gio-sections.txt
gio/ChangeLog
gio/gbufferedoutputstream.c
gio/gfilterinputstream.c
gio/gfilterinputstream.h
gio/gfilteroutputstream.c
gio/gfilteroutputstream.h
gio/gio.symbols
gio/tests/.gitignore
gio/tests/Makefile.am
gio/tests/filter-streams.c [new file with mode: 0644]

index b2754bdc60273a41e9b662a81720bd270082eb1a..6018131536b51f6e598526a8071d98852d7c1355 100644 (file)
@@ -518,6 +518,8 @@ GFileInputStreamPrivate
 <TITLE>GFilterInputStream</TITLE>
 GFilterInputStream
 g_filter_input_stream_get_base_stream
+g_filter_input_stream_get_close_base_stream
+g_filter_input_stream_set_close_base_stream
 <SUBSECTION Standard>
 GFilterInputStreamClass
 G_FILTER_INPUT_STREAM
@@ -694,6 +696,8 @@ GFileOutputStreamPrivate
 <TITLE>GFilterOutputStream</TITLE>
 GFilterOutputStream
 g_filter_output_stream_get_base_stream
+g_filter_output_stream_get_close_base_stream
+g_filter_output_stream_set_close_base_stream
 <SUBSECTION Standard>
 GFilterOutputStreamClass
 G_FILTER_OUTPUT_STREAM
index 4c89223463f54aeda22461019b5d7b8a9b8f1d82..e5ded976701602481472070d09f3f10430506859 100644 (file)
@@ -1,3 +1,26 @@
+2009-01-20  Ryan Lortie  <desrt@desrt.ca>
+
+       Bug 568394 – dropping the last reference to a stream filter closes the
+       base stream
+
+       * gfilterinputstream.h:
+       * gfilterinputstream.c: add "close-base-stream" property and only
+       close the base stream if it is true.  issue async close callbacks from
+       correct source object.
+       * gfilteroutputstream.h:
+       * gfilteroutputstream.c: add a "close-base-stream" property and only
+       close the base stream if it is true.  issue async close callbacks from
+       correct source object.
+       * gbufferedoutputstream: check g_filter_output_stream_get_close_base()
+       before closing the base stream.  fix invalid source tag comparison in
+       close_async (was comparing to flush_async).
+       * ../docs/reference/gio/gio-sections.txt:
+       * gio.symbols: add
+       g_filter_{in,out}put_stream_{g,s}et_close_base_stream
+       * tests/filter-streams.c: new test cases
+       * tests/Makefile.am: add new test
+       * tests/.gitignore: add new test
+
 2009-01-19  Matthias Clasen  <mclasen@redhat.com>
 
        * gdesktopappinfo.c (g_desktop_app_info_new): Expand the docs.
index 92b5f43b9dd8198d228ba9c473790579d69fb1cd..5bb6a987bfe86f92a253c3b456ff2ff7af8f0e2c 100644 (file)
@@ -508,11 +508,14 @@ g_buffered_output_stream_close (GOutputStream  *stream,
 
   res = flush_buffer (bstream, cancellable, error);
 
-  /* report the first error but still close the stream */
-  if (res)
-    res = g_output_stream_close (base_stream, cancellable, error); 
-  else
-    g_output_stream_close (base_stream, cancellable, NULL); 
+  if (g_filter_output_stream_get_close_base_stream (G_FILTER_OUTPUT_STREAM (stream)))
+    {
+      /* report the first error but still close the stream */
+      if (res)
+        res = g_output_stream_close (base_stream, cancellable, error); 
+      else
+        g_output_stream_close (base_stream, cancellable, NULL); 
+    }
 
   return res;
 }
@@ -569,10 +572,13 @@ flush_buffer_thread (GSimpleAsyncResult *result,
       /* if flushing the buffer or the stream returned 
        * an error report that first error but still try 
        * close the stream */
-      if (res == FALSE)
-        g_output_stream_close (base_stream, cancellable, NULL);
-      else 
-        res = g_output_stream_close (base_stream, cancellable, &error);
+      if (g_filter_output_stream_get_close_base_stream (G_FILTER_OUTPUT_STREAM (stream)))
+        {
+          if (res == FALSE)
+            g_output_stream_close (base_stream, cancellable, NULL);
+          else 
+            res = g_output_stream_close (base_stream, cancellable, &error);
+        }
     }
 
   if (res == FALSE)
@@ -758,7 +764,7 @@ g_buffered_output_stream_close_finish (GOutputStream        *stream,
   simple = G_SIMPLE_ASYNC_RESULT (result);
 
   g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == 
-            g_buffered_output_stream_flush_async);
+            g_buffered_output_stream_close_async);
 
   return TRUE;
 }
index 14b7cde0e09cce5585c40e333bbb13d4d6e0ab58..86d001ef5aa6225c6072ba9afc989ce66263fc15 100644 (file)
@@ -23,6 +23,7 @@
 #include "config.h"
 #include "gfilterinputstream.h"
 #include "ginputstream.h"
+#include "gsimpleasyncresult.h"
 #include "glibintl.h"
 
 #include "gioalias.h"
@@ -36,7 +37,8 @@
 
 enum {
   PROP_0,
-  PROP_BASE_STREAM
+  PROP_BASE_STREAM,
+  PROP_CLOSE_BASE
 };
 
 static void     g_filter_input_stream_set_property (GObject      *object,
@@ -93,6 +95,13 @@ static gboolean g_filter_input_stream_close_finish (GInputStream         *stream
 
 G_DEFINE_TYPE (GFilterInputStream, g_filter_input_stream, G_TYPE_INPUT_STREAM)
 
+#define GET_PRIVATE(inst) G_TYPE_INSTANCE_GET_PRIVATE (inst, \
+  G_TYPE_FILTER_INPUT_STREAM, GFilterInputStreamPrivate)
+
+typedef struct
+{
+  gboolean close_base;
+} GFilterInputStreamPrivate;
 
 static void
 g_filter_input_stream_class_init (GFilterInputStreamClass *klass)
@@ -117,6 +126,8 @@ g_filter_input_stream_class_init (GFilterInputStreamClass *klass)
   istream_class->close_async  = g_filter_input_stream_close_async;
   istream_class->close_finish = g_filter_input_stream_close_finish;
 
+  g_type_class_add_private (klass, sizeof (GFilterInputStreamPrivate));
+
   g_object_class_install_property (object_class,
                                    PROP_BASE_STREAM,
                                    g_param_spec_object ("base-stream",
@@ -126,6 +137,13 @@ g_filter_input_stream_class_init (GFilterInputStreamClass *klass)
                                                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | 
                                                          G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB));
 
+  g_object_class_install_property (object_class,
+                                   PROP_CLOSE_BASE,
+                                   g_param_spec_boolean ("close-base-stream",
+                                                         P_("Close Base Stream"),
+                                                         P_("If the base stream be closed when the filter stream is"),
+                                                         TRUE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | 
+                                                         G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB));
 }
 
 static void
@@ -146,6 +164,11 @@ g_filter_input_stream_set_property (GObject         *object,
       filter_stream->base_stream = G_INPUT_STREAM (obj); 
       break;
 
+    case PROP_CLOSE_BASE:
+      g_filter_input_stream_set_close_base_stream (filter_stream,
+                                                   g_value_get_boolean (value));
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -169,6 +192,10 @@ g_filter_input_stream_get_property (GObject    *object,
       g_value_set_object (value, filter_stream->base_stream);
       break;
 
+    case PROP_CLOSE_BASE:
+      g_value_set_boolean (value, GET_PRIVATE (filter_stream)->close_base);
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -210,6 +237,49 @@ g_filter_input_stream_get_base_stream (GFilterInputStream *stream)
   return stream->base_stream;
 }
 
+/**
+ * g_filter_input_stream_get_close_base_stream:
+ * @stream: a #GFilterInputStream.
+ *
+ * Returns whether the base stream will be closed when @stream is
+ * closed.
+ *
+ * Return value: %TRUE if the base stream will be closed.
+ **/
+gboolean
+g_filter_input_stream_get_close_base_stream (GFilterInputStream *stream)
+{
+  g_return_val_if_fail (G_IS_FILTER_INPUT_STREAM (stream), FALSE);
+
+  return GET_PRIVATE (stream)->close_base;
+}
+
+/**
+ * g_filter_input_stream_set_close_base_stream:
+ * @stream: a #GFilterInputStream.
+ * @close_base: %TRUE to close the base stream.
+ *
+ * Sets whether the base stream will be closed when @stream is closed.
+ **/
+void
+g_filter_input_stream_set_close_base_stream (GFilterInputStream *stream,
+                                             gboolean            close_base)
+{
+  GFilterInputStreamPrivate *priv;
+
+  g_return_if_fail (G_IS_FILTER_INPUT_STREAM (stream));
+
+  close_base = !!close_base;
+  priv = GET_PRIVATE (stream);
+
+  if (priv->close_base != close_base)
+    {
+      priv->close_base = close_base;
+      g_object_notify (G_OBJECT (stream), "close-base-stream");
+    }
+}
+
 static gssize
 g_filter_input_stream_read (GInputStream  *stream,
                             void          *buffer,
@@ -258,16 +328,20 @@ g_filter_input_stream_close (GInputStream  *stream,
                              GCancellable  *cancellable,
                              GError       **error)
 {
-  GFilterInputStream *filter_stream;
-  GInputStream       *base_stream;
-  gboolean            res;
+  gboolean res = TRUE;
 
-  filter_stream = G_FILTER_INPUT_STREAM (stream);
-  base_stream = filter_stream->base_stream;
+  if (GET_PRIVATE (stream)->close_base)
+    {
+      GFilterInputStream *filter_stream;
+      GInputStream       *base_stream;
 
-  res = g_input_stream_close (base_stream,
-                              cancellable,
-                              error);
+      filter_stream = G_FILTER_INPUT_STREAM (stream);
+      base_stream = filter_stream->base_stream;
+
+      res = g_input_stream_close (base_stream,
+                                  cancellable,
+                                  error);
+    }
 
   return res;
 }
@@ -357,6 +431,26 @@ g_filter_input_stream_skip_finish (GInputStream  *stream,
   return nskipped;
 }
 
+static void
+g_filter_input_stream_close_ready (GObject       *object,
+                                   GAsyncResult  *result,
+                                   gpointer       user_data)
+{
+  GSimpleAsyncResult *simple = user_data;
+  GError *error = NULL;
+
+  g_input_stream_close_finish (G_INPUT_STREAM (object), result, &error);
+
+  if (error)
+    {
+      g_simple_async_result_set_from_error (simple, error);
+      g_error_free (error);
+    }
+
+  g_simple_async_result_complete (simple);
+  g_object_unref (simple);
+}
+
 static void
 g_filter_input_stream_close_async (GInputStream        *stream,
                                    int                  io_priority,
@@ -364,17 +458,26 @@ g_filter_input_stream_close_async (GInputStream        *stream,
                                    GAsyncReadyCallback  callback,
                                    gpointer             user_data)
 {
-  GFilterInputStream *filter_stream;
-  GInputStream       *base_stream;
+  GSimpleAsyncResult *simple;
 
-  filter_stream = G_FILTER_INPUT_STREAM (stream);
-  base_stream = filter_stream->base_stream;
+  simple = g_simple_async_result_new (G_OBJECT (stream),
+                                      callback, user_data,
+                                      g_filter_input_stream_close_async);
+
+  if (GET_PRIVATE (stream)->close_base)
+    {
+      GFilterInputStream *filter_stream = G_FILTER_INPUT_STREAM (stream);
 
-  g_input_stream_close_async (base_stream,
-                              io_priority,
-                              cancellable,
-                              callback,
-                              user_data);
+      g_input_stream_close_async (filter_stream->base_stream,
+                                  io_priority, cancellable,
+                                  g_filter_input_stream_close_ready,
+                                  g_object_ref (simple));
+    }
+  else
+    /* do nothing */
+    g_simple_async_result_complete_in_idle (simple);
+
+  g_object_unref (simple);
 }
 
 static gboolean
@@ -382,18 +485,14 @@ g_filter_input_stream_close_finish (GInputStream  *stream,
                                     GAsyncResult  *result,
                                     GError       **error)
 {
-  GFilterInputStream *filter_stream;
-  GInputStream       *base_stream;
-  gboolean res;
+  GSimpleAsyncResult *simple;
 
-  filter_stream = G_FILTER_INPUT_STREAM (stream);
-  base_stream = filter_stream->base_stream;
+  g_return_val_if_fail (g_simple_async_result_is_valid (
+    result, G_OBJECT (stream), g_filter_input_stream_close_async), FALSE);
 
-  res = g_input_stream_close_finish (stream,
-                                     result,
-                                     error);
+  simple = G_SIMPLE_ASYNC_RESULT (result);
 
-  return res;
+  return !g_simple_async_result_propagate_error (simple, error);
 }
 
 #define __G_FILTER_INPUT_STREAM_C__
index 91ba7b53a0ef00e37f80725cbf4e376d4066d8c1..1514104aa480db96b71776e9d2ea10d44d57baf4 100644 (file)
@@ -44,7 +44,6 @@ G_BEGIN_DECLS
  * A base class for all input streams that work on an underlying stream.
  **/
 typedef struct _GFilterInputStreamClass    GFilterInputStreamClass;
-typedef struct _GFilterInputStreamPrivate  GFilterInputStreamPrivate;
 
 struct _GFilterInputStream
 {
@@ -66,8 +65,11 @@ struct _GFilterInputStreamClass
 };
 
 
-GType          g_filter_input_stream_get_type        (void) G_GNUC_CONST;
-GInputStream * g_filter_input_stream_get_base_stream (GFilterInputStream *stream);
+GType          g_filter_input_stream_get_type              (void) G_GNUC_CONST;
+GInputStream * g_filter_input_stream_get_base_stream       (GFilterInputStream *stream);
+gboolean       g_filter_input_stream_get_close_base_stream (GFilterInputStream *stream);
+void           g_filter_input_stream_set_close_base_stream (GFilterInputStream *stream,
+                                                            gboolean            close_base);
 
 G_END_DECLS
 
index 9881ab1803d1a54ac462402cce480c40fd2d3a3e..8706941c35ed45dbeac275e49dcd6a60f34298dd 100644 (file)
@@ -22,6 +22,7 @@
 
 #include "config.h"
 #include "gfilteroutputstream.h"
+#include "gsimpleasyncresult.h"
 #include "goutputstream.h"
 #include "glibintl.h"
 
@@ -36,7 +37,8 @@
 
 enum {
   PROP_0,
-  PROP_BASE_STREAM
+  PROP_BASE_STREAM,
+  PROP_CLOSE_BASE
 };
 
 static void     g_filter_output_stream_set_property (GObject      *object,
@@ -93,7 +95,13 @@ static gboolean g_filter_output_stream_close_finish (GOutputStream        *strea
 
 G_DEFINE_TYPE (GFilterOutputStream, g_filter_output_stream, G_TYPE_OUTPUT_STREAM)
 
+#define GET_PRIVATE(inst) G_TYPE_INSTANCE_GET_PRIVATE (inst, \
+  G_TYPE_FILTER_OUTPUT_STREAM, GFilterOutputStreamPrivate)
 
+typedef struct
+{
+  gboolean close_base;
+} GFilterOutputStreamPrivate;
 
 static void
 g_filter_output_stream_class_init (GFilterOutputStreamClass *klass)
@@ -117,6 +125,8 @@ g_filter_output_stream_class_init (GFilterOutputStreamClass *klass)
   ostream_class->close_async  = g_filter_output_stream_close_async;
   ostream_class->close_finish = g_filter_output_stream_close_finish;
 
+  g_type_class_add_private (klass, sizeof (GFilterOutputStreamPrivate));
+
   g_object_class_install_property (object_class,
                                    PROP_BASE_STREAM,
                                    g_param_spec_object ("base-stream",
@@ -126,6 +136,13 @@ g_filter_output_stream_class_init (GFilterOutputStreamClass *klass)
                                                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | 
                                                          G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB));
 
+  g_object_class_install_property (object_class,
+                                   PROP_CLOSE_BASE,
+                                   g_param_spec_boolean ("close-base-stream",
+                                                         P_("Close Base Stream"),
+                                                         P_("If the base stream be closed when the filter stream is"),
+                                                         TRUE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+                                                         G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB));
 }
 
 static void
@@ -146,6 +163,11 @@ g_filter_output_stream_set_property (GObject      *object,
       filter_stream->base_stream = G_OUTPUT_STREAM (obj);
       break;
 
+    case PROP_CLOSE_BASE:
+      g_filter_output_stream_set_close_base_stream (filter_stream,
+                                                    g_value_get_boolean (value));
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -169,6 +191,10 @@ g_filter_output_stream_get_property (GObject    *object,
       g_value_set_object (value, filter_stream->base_stream);
       break;
 
+    case PROP_CLOSE_BASE:
+      g_value_set_boolean (value, GET_PRIVATE (filter_stream)->close_base);
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -214,6 +240,49 @@ g_filter_output_stream_get_base_stream (GFilterOutputStream *stream)
   return stream->base_stream;
 }
 
+/**
+ * g_filter_output_stream_get_close_base_stream:
+ * @stream: a #GFilterOutputStream.
+ *
+ * Returns whether the base stream will be closed when @stream is
+ * closed.
+ *
+ * Return value: %TRUE if the base stream will be closed.
+ **/
+gboolean
+g_filter_output_stream_get_close_base_stream (GFilterOutputStream *stream)
+{
+  g_return_val_if_fail (G_IS_FILTER_OUTPUT_STREAM (stream), FALSE);
+
+  return GET_PRIVATE (stream)->close_base;
+}
+
+/**
+ * g_filter_output_stream_set_close_base_stream:
+ * @stream: a #GFilterOutputStream.
+ * @close_base: %TRUE to close the base stream.
+ *
+ * Sets whether the base stream will be closed when @stream is closed.
+ **/
+void
+g_filter_output_stream_set_close_base_stream (GFilterOutputStream *stream,
+                                              gboolean             close_base)
+{
+  GFilterOutputStreamPrivate *priv;
+
+  g_return_if_fail (G_IS_FILTER_OUTPUT_STREAM (stream));
+
+  close_base = !!close_base;
+
+  priv = GET_PRIVATE (stream);
+
+  if (priv->close_base != close_base)
+    {
+      priv->close_base = close_base;
+      g_object_notify (G_OBJECT (stream), "close-base-stream");
+    }
+}
+
 static gssize
 g_filter_output_stream_write (GOutputStream  *stream,
                               const void     *buffer,
@@ -257,14 +326,18 @@ g_filter_output_stream_close (GOutputStream  *stream,
                               GCancellable   *cancellable,
                               GError        **error)
 {
-  GFilterOutputStream *filter_stream;
-  gboolean res;
+  gboolean res = TRUE;
 
-  filter_stream = G_FILTER_OUTPUT_STREAM (stream);
+  if (GET_PRIVATE (stream)->close_base)
+    {
+      GFilterOutputStream *filter_stream;
 
-  res = g_output_stream_close (filter_stream->base_stream,
-                               cancellable,
-                               error);
+      filter_stream = G_FILTER_OUTPUT_STREAM (stream);
+
+      res = g_output_stream_close (filter_stream->base_stream,
+                                   cancellable,
+                                   error);
+    }
 
   return res;
 }
@@ -344,22 +417,53 @@ g_filter_output_stream_flush_finish (GOutputStream  *stream,
   return res;
 }
 
+static void
+g_filter_output_stream_close_ready (GObject       *object,
+                                    GAsyncResult  *result,
+                                    gpointer       user_data)
+{
+  GSimpleAsyncResult *simple = user_data;
+  GError *error = NULL;
+
+  g_output_stream_close_finish (G_OUTPUT_STREAM (object), result, &error);
+
+  if (error)
+    {
+      g_simple_async_result_set_from_error (simple, error);
+      g_error_free (error);
+    }
+
+  g_simple_async_result_complete (simple);
+  g_object_unref (simple);
+}
+
 static void
 g_filter_output_stream_close_async (GOutputStream       *stream,
                                     int                  io_priority,
                                     GCancellable        *cancellable,
                                     GAsyncReadyCallback  callback,
-                                    gpointer             data)
+                                    gpointer             user_data)
 {
-  GFilterOutputStream *filter_stream;
+  GSimpleAsyncResult *simple;
 
-  filter_stream = G_FILTER_OUTPUT_STREAM (stream);
+  simple = g_simple_async_result_new (G_OBJECT (stream),
+                                      callback, user_data,
+                                      g_filter_output_stream_close_async);
 
-  g_output_stream_close_async (filter_stream->base_stream,
-                               io_priority,
-                               cancellable,
-                               callback,
-                               data);
+  if (GET_PRIVATE (stream)->close_base)
+    {
+      GFilterOutputStream *filter_stream = G_FILTER_OUTPUT_STREAM (stream);
+
+      g_output_stream_close_async (filter_stream->base_stream,
+                                  io_priority, cancellable,
+                                  g_filter_output_stream_close_ready,
+                                  g_object_ref (simple));
+    }
+  else
+    /* do nothing */
+    g_simple_async_result_complete_in_idle (simple);
+
+  g_object_unref (simple);
 }
 
 static gboolean
@@ -367,16 +471,14 @@ g_filter_output_stream_close_finish (GOutputStream  *stream,
                                      GAsyncResult   *result,
                                      GError        **error)
 {
-  GFilterOutputStream *filter_stream;
-  gboolean res;
+  GSimpleAsyncResult *simple;
 
-  filter_stream = G_FILTER_OUTPUT_STREAM (stream);
+  g_return_val_if_fail (g_simple_async_result_is_valid (
+    result, G_OBJECT (stream), g_filter_output_stream_close_async), FALSE);
 
-  res = g_output_stream_close_finish (filter_stream->base_stream,
-                                      result,
-                                      error);
+  simple = G_SIMPLE_ASYNC_RESULT (result);
 
-  return res;
+  return !g_simple_async_result_propagate_error (simple, error);
 }
 
 #define __G_FILTER_OUTPUT_STREAM_C__
index 6ccb0d4c7959d2741889a7be7302bfdcc34ae662..10350e5a027d679bf3d9abe4d08b37c109835e61 100644 (file)
@@ -44,7 +44,6 @@ G_BEGIN_DECLS
  * A base class for all output streams that work on an underlying stream.
  **/
 typedef struct _GFilterOutputStreamClass    GFilterOutputStreamClass;
-typedef struct _GFilterOutputStreamPrivate  GFilterOutputStreamPrivate;
 
 struct _GFilterOutputStream
 {
@@ -66,8 +65,11 @@ struct _GFilterOutputStreamClass
 };
 
 
-GType           g_filter_output_stream_get_type        (void) G_GNUC_CONST;
-GOutputStream * g_filter_output_stream_get_base_stream (GFilterOutputStream *stream);
+GType           g_filter_output_stream_get_type              (void) G_GNUC_CONST;
+GOutputStream * g_filter_output_stream_get_base_stream       (GFilterOutputStream *stream);
+gboolean        g_filter_output_stream_get_close_base_stream (GFilterOutputStream *stream);
+void            g_filter_output_stream_set_close_base_stream (GFilterOutputStream *stream,
+                                                              gboolean             close_base);
 
 G_END_DECLS
 
index de2268be6b5df42a61e631c5231fb6a6ae7fd1c2..2e8a363ca25f3a26e089fec2be2d11ae5c640855 100644 (file)
@@ -456,6 +456,8 @@ g_file_output_stream_get_etag
 #if IN_FILE(__G_FILTER_INPUT_STREAM_C__)
 g_filter_input_stream_get_type  G_GNUC_CONST
 g_filter_input_stream_get_base_stream 
+g_filter_input_stream_get_close_base_stream
+g_filter_input_stream_set_close_base_stream
 #endif
 #endif
 
@@ -463,6 +465,8 @@ g_filter_input_stream_get_base_stream
 #if IN_FILE(__G_FILTER_OUTPUT_STREAM_C__)
 g_filter_output_stream_get_type  G_GNUC_CONST
 g_filter_output_stream_get_base_stream 
+g_filter_output_stream_get_close_base_stream
+g_filter_output_stream_set_close_base_stream
 #endif
 #endif
 
index e770aaa2d97623f59dd3963c359d6cad700c6db7..8985d06e2a5ff88286654a7de875df4cc52639b5 100644 (file)
@@ -10,3 +10,4 @@ g-file-info
 live-g-file
 memory-input-stream
 memory-output-stream
+filter-streams
index abfe8836e5129d07fca12e542ad6eccb42592f36..5e974a4a65f8f5010585929c876c478f8adc1973 100644 (file)
@@ -25,6 +25,7 @@ TEST_PROGS +=                 \
        data-output-stream      \
        g-icon                  \
        buffered-input-stream   \
+       filter-streams          \
        simple-async-result
 
 if OS_UNIX
@@ -68,4 +69,7 @@ unix_streams_LDADD      = $(progs_ldadd) \
 simple_async_result_SOURCES    = simple-async-result.c
 simple_async_result_LDADD      = $(progs_ldadd)
 
+filter_streams_SOURCES         = filter-streams.c
+filter_streams_LDADD           = $(progs_ldadd)
+
 DISTCLEAN_FILES = applications/mimeinfo.cache
diff --git a/gio/tests/filter-streams.c b/gio/tests/filter-streams.c
new file mode 100644 (file)
index 0000000..fab17ff
--- /dev/null
@@ -0,0 +1,239 @@
+/*
+ * Copyright © 2009 Codethink Limited
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2 of the licence or (at
+ * your option) any later version.
+ *
+ * See the included COPYING file for more information.
+ *
+ * Author: Ryan Lortie <desrt@desrt.ca>
+ */
+
+#include <glib/glib.h>
+#include <gio/gio.h>
+
+static void
+test_input_filter (void)
+{
+  GInputStream *base, *f1, *f2;
+
+  g_test_bug ("568394");
+  base = g_memory_input_stream_new_from_data ("abcdefghijk", -1, NULL);
+  f1 = g_buffered_input_stream_new (base);
+  f2 = g_buffered_input_stream_new (base);
+
+  g_filter_input_stream_set_close_base_stream (G_FILTER_INPUT_STREAM (f1), FALSE);
+
+  g_assert (g_filter_input_stream_get_base_stream (G_FILTER_INPUT_STREAM (f1)) == base);
+  g_assert (g_filter_input_stream_get_base_stream (G_FILTER_INPUT_STREAM (f2)) == base);
+
+  g_assert (!g_input_stream_is_closed (base));
+  g_assert (!g_input_stream_is_closed (f1));
+  g_assert (!g_input_stream_is_closed (f2));
+
+  g_object_unref (f1);
+
+  g_assert (!g_input_stream_is_closed (base));
+  g_assert (!g_input_stream_is_closed (f2));
+
+  g_object_unref (f2);
+
+  g_assert (g_input_stream_is_closed (base));
+
+  g_object_unref (base);
+}
+
+static void
+test_output_filter (void)
+{
+  GOutputStream *base, *f1, *f2;
+
+  base = g_memory_output_stream_new (NULL, 0, g_realloc, g_free);
+  f1 = g_buffered_output_stream_new (base);
+  f2 = g_buffered_output_stream_new (base);
+
+  g_filter_output_stream_set_close_base_stream (G_FILTER_OUTPUT_STREAM (f1), FALSE);
+
+  g_assert (g_filter_output_stream_get_base_stream (G_FILTER_OUTPUT_STREAM (f1)) == base);
+  g_assert (g_filter_output_stream_get_base_stream (G_FILTER_OUTPUT_STREAM (f2)) == base);
+
+  g_assert (!g_output_stream_is_closed (base));
+  g_assert (!g_output_stream_is_closed (f1));
+  g_assert (!g_output_stream_is_closed (f2));
+
+  g_object_unref (f1);
+
+  g_assert (!g_output_stream_is_closed (base));
+  g_assert (!g_output_stream_is_closed (f2));
+
+  g_object_unref (f2);
+
+  g_assert (g_output_stream_is_closed (base));
+
+  g_object_unref (base);
+}
+
+gpointer expected_obj;
+gpointer expected_data;
+gboolean callback_happened;
+
+static void
+in_cb (GObject      *object,
+       GAsyncResult *result,
+       gpointer      user_data)
+{
+  GError *error = NULL;
+
+  g_assert (object == expected_obj);
+  g_assert (user_data == expected_data);
+  g_assert (callback_happened == FALSE);
+
+  g_input_stream_close_finish (expected_obj, result, &error);
+  g_assert (error == NULL);
+
+  callback_happened = TRUE;
+}
+
+static void
+test_input_async (void)
+{
+  GInputStream *base, *f1, *f2;
+
+  base = g_memory_input_stream_new_from_data ("abcdefghijk", -1, NULL);
+  f1 = g_buffered_input_stream_new (base);
+  f2 = g_buffered_input_stream_new (base);
+
+  g_filter_input_stream_set_close_base_stream (G_FILTER_INPUT_STREAM (f1), FALSE);
+
+  g_assert (g_filter_input_stream_get_base_stream (G_FILTER_INPUT_STREAM (f1)) == base);
+  g_assert (g_filter_input_stream_get_base_stream (G_FILTER_INPUT_STREAM (f2)) == base);
+
+  g_assert (!g_input_stream_is_closed (base));
+  g_assert (!g_input_stream_is_closed (f1));
+  g_assert (!g_input_stream_is_closed (f2));
+
+  expected_obj = f1;
+  expected_data = g_malloc (20);
+  callback_happened = FALSE;
+  g_input_stream_close_async (f1, 0, NULL, in_cb, expected_data);
+
+  g_assert (callback_happened == FALSE);
+  while (g_main_context_pending (NULL))
+    g_main_context_iteration (NULL, FALSE);
+  g_assert (callback_happened == TRUE);
+
+  g_assert (!g_input_stream_is_closed (base));
+  g_assert (!g_input_stream_is_closed (f2));
+  g_free (expected_data);
+  g_object_unref (f1);
+  g_assert (!g_input_stream_is_closed (base));
+  g_assert (!g_input_stream_is_closed (f2));
+
+  expected_obj = f2;
+  expected_data = g_malloc (20);
+  callback_happened = FALSE;
+  g_input_stream_close_async (f2, 0, NULL, in_cb, expected_data);
+
+  g_assert (callback_happened == FALSE);
+  while (g_main_context_pending (NULL))
+    g_main_context_iteration (NULL, FALSE);
+  g_assert (callback_happened == TRUE);
+
+  g_assert (g_input_stream_is_closed (base));
+  g_assert (g_input_stream_is_closed (f2));
+  g_free (expected_data);
+  g_object_unref (f2);
+
+  g_assert (g_input_stream_is_closed (base));
+  g_object_unref (base);
+}
+
+static void
+out_cb (GObject      *object,
+        GAsyncResult *result,
+        gpointer      user_data)
+{
+  GError *error = NULL;
+
+  g_assert (object == expected_obj);
+  g_assert (user_data == expected_data);
+  g_assert (callback_happened == FALSE);
+
+  g_output_stream_close_finish (expected_obj, result, &error);
+  g_assert (error == NULL);
+
+  callback_happened = TRUE;
+}
+
+
+static void
+test_output_async (void)
+{
+  GOutputStream *base, *f1, *f2;
+
+  base = g_memory_output_stream_new (NULL, 0, g_realloc, g_free);
+  f1 = g_buffered_output_stream_new (base);
+  f2 = g_buffered_output_stream_new (base);
+
+  g_filter_output_stream_set_close_base_stream (G_FILTER_OUTPUT_STREAM (f1), FALSE);
+
+  g_assert (g_filter_output_stream_get_base_stream (G_FILTER_OUTPUT_STREAM (f1)) == base);
+  g_assert (g_filter_output_stream_get_base_stream (G_FILTER_OUTPUT_STREAM (f2)) == base);
+
+  g_assert (!g_output_stream_is_closed (base));
+  g_assert (!g_output_stream_is_closed (f1));
+  g_assert (!g_output_stream_is_closed (f2));
+
+  expected_obj = f1;
+  expected_data = g_malloc (20);
+  callback_happened = FALSE;
+  g_output_stream_close_async (f1, 0, NULL, out_cb, expected_data);
+
+  g_assert (callback_happened == FALSE);
+  while (g_main_context_pending (NULL))
+    g_main_context_iteration (NULL, FALSE);
+  g_assert (callback_happened == TRUE);
+
+  g_assert (!g_output_stream_is_closed (base));
+  g_assert (!g_output_stream_is_closed (f2));
+  g_free (expected_data);
+  g_object_unref (f1);
+  g_assert (!g_output_stream_is_closed (base));
+  g_assert (!g_output_stream_is_closed (f2));
+
+  expected_obj = f2;
+  expected_data = g_malloc (20);
+  callback_happened = FALSE;
+  g_output_stream_close_async (f2, 0, NULL, out_cb, expected_data);
+
+  g_assert (callback_happened == FALSE);
+  while (g_main_context_pending (NULL))
+    g_main_context_iteration (NULL, FALSE);
+  g_assert (callback_happened == TRUE);
+
+  g_assert (g_output_stream_is_closed (base));
+  g_assert (g_output_stream_is_closed (f2));
+  g_free (expected_data);
+  g_object_unref (f2);
+
+  g_assert (g_output_stream_is_closed (base));
+  g_object_unref (base);
+}
+
+
+int
+main (int argc, char **argv)
+{
+  g_test_init (&argc, &argv, NULL);
+  g_test_bug_base ("http://bugzilla.gnome.org/");
+
+  g_type_init ();
+  g_test_add_func ("/filter-stream/input", test_input_filter);
+  g_test_add_func ("/filter-stream/output", test_output_filter);
+  g_test_add_func ("/filter-stream/async-input", test_input_async);
+  g_test_add_func ("/filter-stream/async-output", test_output_async);
+
+  return g_test_run();
+}