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

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
This commit is contained in:
Ryan Lortie 2009-01-21 14:09:56 +00:00 committed by Ryan Lortie
parent 4a22510022
commit a9c978a354
11 changed files with 551 additions and 65 deletions

View File

@ -518,6 +518,8 @@ GFileInputStreamPrivate
<TITLE>GFilterInputStream</TITLE> <TITLE>GFilterInputStream</TITLE>
GFilterInputStream GFilterInputStream
g_filter_input_stream_get_base_stream 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> <SUBSECTION Standard>
GFilterInputStreamClass GFilterInputStreamClass
G_FILTER_INPUT_STREAM G_FILTER_INPUT_STREAM
@ -694,6 +696,8 @@ GFileOutputStreamPrivate
<TITLE>GFilterOutputStream</TITLE> <TITLE>GFilterOutputStream</TITLE>
GFilterOutputStream GFilterOutputStream
g_filter_output_stream_get_base_stream 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> <SUBSECTION Standard>
GFilterOutputStreamClass GFilterOutputStreamClass
G_FILTER_OUTPUT_STREAM G_FILTER_OUTPUT_STREAM

View 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> 2009-01-19 Matthias Clasen <mclasen@redhat.com>
* gdesktopappinfo.c (g_desktop_app_info_new): Expand the docs. * gdesktopappinfo.c (g_desktop_app_info_new): Expand the docs.

View File

@ -508,11 +508,14 @@ g_buffered_output_stream_close (GOutputStream *stream,
res = flush_buffer (bstream, cancellable, error); res = flush_buffer (bstream, cancellable, error);
if (g_filter_output_stream_get_close_base_stream (G_FILTER_OUTPUT_STREAM (stream)))
{
/* report the first error but still close the stream */ /* report the first error but still close the stream */
if (res) if (res)
res = g_output_stream_close (base_stream, cancellable, error); res = g_output_stream_close (base_stream, cancellable, error);
else else
g_output_stream_close (base_stream, cancellable, NULL); g_output_stream_close (base_stream, cancellable, NULL);
}
return res; return res;
} }
@ -569,11 +572,14 @@ flush_buffer_thread (GSimpleAsyncResult *result,
/* if flushing the buffer or the stream returned /* if flushing the buffer or the stream returned
* an error report that first error but still try * an error report that first error but still try
* close the stream */ * close the stream */
if (g_filter_output_stream_get_close_base_stream (G_FILTER_OUTPUT_STREAM (stream)))
{
if (res == FALSE) if (res == FALSE)
g_output_stream_close (base_stream, cancellable, NULL); g_output_stream_close (base_stream, cancellable, NULL);
else else
res = g_output_stream_close (base_stream, cancellable, &error); res = g_output_stream_close (base_stream, cancellable, &error);
} }
}
if (res == FALSE) if (res == FALSE)
{ {
@ -758,7 +764,7 @@ g_buffered_output_stream_close_finish (GOutputStream *stream,
simple = G_SIMPLE_ASYNC_RESULT (result); simple = G_SIMPLE_ASYNC_RESULT (result);
g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == 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; return TRUE;
} }

View File

@ -23,6 +23,7 @@
#include "config.h" #include "config.h"
#include "gfilterinputstream.h" #include "gfilterinputstream.h"
#include "ginputstream.h" #include "ginputstream.h"
#include "gsimpleasyncresult.h"
#include "glibintl.h" #include "glibintl.h"
#include "gioalias.h" #include "gioalias.h"
@ -36,7 +37,8 @@
enum { enum {
PROP_0, PROP_0,
PROP_BASE_STREAM PROP_BASE_STREAM,
PROP_CLOSE_BASE
}; };
static void g_filter_input_stream_set_property (GObject *object, 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) 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 static void
g_filter_input_stream_class_init (GFilterInputStreamClass *klass) 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_async = g_filter_input_stream_close_async;
istream_class->close_finish = g_filter_input_stream_close_finish; 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, g_object_class_install_property (object_class,
PROP_BASE_STREAM, PROP_BASE_STREAM,
g_param_spec_object ("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_READWRITE | G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB)); 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 static void
@ -146,6 +164,11 @@ g_filter_input_stream_set_property (GObject *object,
filter_stream->base_stream = G_INPUT_STREAM (obj); filter_stream->base_stream = G_INPUT_STREAM (obj);
break; break;
case PROP_CLOSE_BASE:
g_filter_input_stream_set_close_base_stream (filter_stream,
g_value_get_boolean (value));
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
@ -169,6 +192,10 @@ g_filter_input_stream_get_property (GObject *object,
g_value_set_object (value, filter_stream->base_stream); g_value_set_object (value, filter_stream->base_stream);
break; break;
case PROP_CLOSE_BASE:
g_value_set_boolean (value, GET_PRIVATE (filter_stream)->close_base);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
@ -210,6 +237,49 @@ g_filter_input_stream_get_base_stream (GFilterInputStream *stream)
return stream->base_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 static gssize
g_filter_input_stream_read (GInputStream *stream, g_filter_input_stream_read (GInputStream *stream,
void *buffer, void *buffer,
@ -257,10 +327,13 @@ static gboolean
g_filter_input_stream_close (GInputStream *stream, g_filter_input_stream_close (GInputStream *stream,
GCancellable *cancellable, GCancellable *cancellable,
GError **error) GError **error)
{
gboolean res = TRUE;
if (GET_PRIVATE (stream)->close_base)
{ {
GFilterInputStream *filter_stream; GFilterInputStream *filter_stream;
GInputStream *base_stream; GInputStream *base_stream;
gboolean res;
filter_stream = G_FILTER_INPUT_STREAM (stream); filter_stream = G_FILTER_INPUT_STREAM (stream);
base_stream = filter_stream->base_stream; base_stream = filter_stream->base_stream;
@ -268,6 +341,7 @@ g_filter_input_stream_close (GInputStream *stream,
res = g_input_stream_close (base_stream, res = g_input_stream_close (base_stream,
cancellable, cancellable,
error); error);
}
return res; return res;
} }
@ -357,6 +431,26 @@ g_filter_input_stream_skip_finish (GInputStream *stream,
return nskipped; 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 static void
g_filter_input_stream_close_async (GInputStream *stream, g_filter_input_stream_close_async (GInputStream *stream,
int io_priority, int io_priority,
@ -364,17 +458,26 @@ g_filter_input_stream_close_async (GInputStream *stream,
GAsyncReadyCallback callback, GAsyncReadyCallback callback,
gpointer user_data) gpointer user_data)
{ {
GFilterInputStream *filter_stream; GSimpleAsyncResult *simple;
GInputStream *base_stream;
filter_stream = G_FILTER_INPUT_STREAM (stream); simple = g_simple_async_result_new (G_OBJECT (stream),
base_stream = filter_stream->base_stream; callback, user_data,
g_filter_input_stream_close_async);
g_input_stream_close_async (base_stream, if (GET_PRIVATE (stream)->close_base)
io_priority, {
cancellable, GFilterInputStream *filter_stream = G_FILTER_INPUT_STREAM (stream);
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 static gboolean
@ -382,18 +485,14 @@ g_filter_input_stream_close_finish (GInputStream *stream,
GAsyncResult *result, GAsyncResult *result,
GError **error) GError **error)
{ {
GFilterInputStream *filter_stream; GSimpleAsyncResult *simple;
GInputStream *base_stream;
gboolean res;
filter_stream = G_FILTER_INPUT_STREAM (stream); g_return_val_if_fail (g_simple_async_result_is_valid (
base_stream = filter_stream->base_stream; result, G_OBJECT (stream), g_filter_input_stream_close_async), FALSE);
res = g_input_stream_close_finish (stream, simple = G_SIMPLE_ASYNC_RESULT (result);
result,
error);
return res; return !g_simple_async_result_propagate_error (simple, error);
} }
#define __G_FILTER_INPUT_STREAM_C__ #define __G_FILTER_INPUT_STREAM_C__

View File

@ -44,7 +44,6 @@ G_BEGIN_DECLS
* A base class for all input streams that work on an underlying stream. * A base class for all input streams that work on an underlying stream.
**/ **/
typedef struct _GFilterInputStreamClass GFilterInputStreamClass; typedef struct _GFilterInputStreamClass GFilterInputStreamClass;
typedef struct _GFilterInputStreamPrivate GFilterInputStreamPrivate;
struct _GFilterInputStream struct _GFilterInputStream
{ {
@ -68,6 +67,9 @@ struct _GFilterInputStreamClass
GType g_filter_input_stream_get_type (void) G_GNUC_CONST; GType g_filter_input_stream_get_type (void) G_GNUC_CONST;
GInputStream * g_filter_input_stream_get_base_stream (GFilterInputStream *stream); 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 G_END_DECLS

View File

@ -22,6 +22,7 @@
#include "config.h" #include "config.h"
#include "gfilteroutputstream.h" #include "gfilteroutputstream.h"
#include "gsimpleasyncresult.h"
#include "goutputstream.h" #include "goutputstream.h"
#include "glibintl.h" #include "glibintl.h"
@ -36,7 +37,8 @@
enum { enum {
PROP_0, PROP_0,
PROP_BASE_STREAM PROP_BASE_STREAM,
PROP_CLOSE_BASE
}; };
static void g_filter_output_stream_set_property (GObject *object, 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) 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 static void
g_filter_output_stream_class_init (GFilterOutputStreamClass *klass) 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_async = g_filter_output_stream_close_async;
ostream_class->close_finish = g_filter_output_stream_close_finish; 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, g_object_class_install_property (object_class,
PROP_BASE_STREAM, PROP_BASE_STREAM,
g_param_spec_object ("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_READWRITE | G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB)); 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 static void
@ -146,6 +163,11 @@ g_filter_output_stream_set_property (GObject *object,
filter_stream->base_stream = G_OUTPUT_STREAM (obj); filter_stream->base_stream = G_OUTPUT_STREAM (obj);
break; break;
case PROP_CLOSE_BASE:
g_filter_output_stream_set_close_base_stream (filter_stream,
g_value_get_boolean (value));
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
@ -169,6 +191,10 @@ g_filter_output_stream_get_property (GObject *object,
g_value_set_object (value, filter_stream->base_stream); g_value_set_object (value, filter_stream->base_stream);
break; break;
case PROP_CLOSE_BASE:
g_value_set_boolean (value, GET_PRIVATE (filter_stream)->close_base);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
@ -214,6 +240,49 @@ g_filter_output_stream_get_base_stream (GFilterOutputStream *stream)
return stream->base_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 static gssize
g_filter_output_stream_write (GOutputStream *stream, g_filter_output_stream_write (GOutputStream *stream,
const void *buffer, const void *buffer,
@ -256,15 +325,19 @@ static gboolean
g_filter_output_stream_close (GOutputStream *stream, g_filter_output_stream_close (GOutputStream *stream,
GCancellable *cancellable, GCancellable *cancellable,
GError **error) GError **error)
{
gboolean res = TRUE;
if (GET_PRIVATE (stream)->close_base)
{ {
GFilterOutputStream *filter_stream; GFilterOutputStream *filter_stream;
gboolean res;
filter_stream = G_FILTER_OUTPUT_STREAM (stream); filter_stream = G_FILTER_OUTPUT_STREAM (stream);
res = g_output_stream_close (filter_stream->base_stream, res = g_output_stream_close (filter_stream->base_stream,
cancellable, cancellable,
error); error);
}
return res; return res;
} }
@ -344,22 +417,53 @@ g_filter_output_stream_flush_finish (GOutputStream *stream,
return res; 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 static void
g_filter_output_stream_close_async (GOutputStream *stream, g_filter_output_stream_close_async (GOutputStream *stream,
int io_priority, int io_priority,
GCancellable *cancellable, GCancellable *cancellable,
GAsyncReadyCallback callback, 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);
if (GET_PRIVATE (stream)->close_base)
{
GFilterOutputStream *filter_stream = G_FILTER_OUTPUT_STREAM (stream);
g_output_stream_close_async (filter_stream->base_stream, g_output_stream_close_async (filter_stream->base_stream,
io_priority, io_priority, cancellable,
cancellable, g_filter_output_stream_close_ready,
callback, g_object_ref (simple));
data); }
else
/* do nothing */
g_simple_async_result_complete_in_idle (simple);
g_object_unref (simple);
} }
static gboolean static gboolean
@ -367,16 +471,14 @@ g_filter_output_stream_close_finish (GOutputStream *stream,
GAsyncResult *result, GAsyncResult *result,
GError **error) GError **error)
{ {
GFilterOutputStream *filter_stream; GSimpleAsyncResult *simple;
gboolean res;
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, simple = G_SIMPLE_ASYNC_RESULT (result);
result,
error);
return res; return !g_simple_async_result_propagate_error (simple, error);
} }
#define __G_FILTER_OUTPUT_STREAM_C__ #define __G_FILTER_OUTPUT_STREAM_C__

View File

@ -44,7 +44,6 @@ G_BEGIN_DECLS
* A base class for all output streams that work on an underlying stream. * A base class for all output streams that work on an underlying stream.
**/ **/
typedef struct _GFilterOutputStreamClass GFilterOutputStreamClass; typedef struct _GFilterOutputStreamClass GFilterOutputStreamClass;
typedef struct _GFilterOutputStreamPrivate GFilterOutputStreamPrivate;
struct _GFilterOutputStream struct _GFilterOutputStream
{ {
@ -68,6 +67,9 @@ struct _GFilterOutputStreamClass
GType g_filter_output_stream_get_type (void) G_GNUC_CONST; GType g_filter_output_stream_get_type (void) G_GNUC_CONST;
GOutputStream * g_filter_output_stream_get_base_stream (GFilterOutputStream *stream); 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 G_END_DECLS

View File

@ -456,6 +456,8 @@ g_file_output_stream_get_etag
#if IN_FILE(__G_FILTER_INPUT_STREAM_C__) #if IN_FILE(__G_FILTER_INPUT_STREAM_C__)
g_filter_input_stream_get_type G_GNUC_CONST g_filter_input_stream_get_type G_GNUC_CONST
g_filter_input_stream_get_base_stream 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
#endif #endif
@ -463,6 +465,8 @@ g_filter_input_stream_get_base_stream
#if IN_FILE(__G_FILTER_OUTPUT_STREAM_C__) #if IN_FILE(__G_FILTER_OUTPUT_STREAM_C__)
g_filter_output_stream_get_type G_GNUC_CONST g_filter_output_stream_get_type G_GNUC_CONST
g_filter_output_stream_get_base_stream 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
#endif #endif

View File

@ -10,3 +10,4 @@ g-file-info
live-g-file live-g-file
memory-input-stream memory-input-stream
memory-output-stream memory-output-stream
filter-streams

View File

@ -25,6 +25,7 @@ TEST_PROGS += \
data-output-stream \ data-output-stream \
g-icon \ g-icon \
buffered-input-stream \ buffered-input-stream \
filter-streams \
simple-async-result simple-async-result
if OS_UNIX if OS_UNIX
@ -68,4 +69,7 @@ unix_streams_LDADD = $(progs_ldadd) \
simple_async_result_SOURCES = simple-async-result.c simple_async_result_SOURCES = simple-async-result.c
simple_async_result_LDADD = $(progs_ldadd) simple_async_result_LDADD = $(progs_ldadd)
filter_streams_SOURCES = filter-streams.c
filter_streams_LDADD = $(progs_ldadd)
DISTCLEAN_FILES = applications/mimeinfo.cache DISTCLEAN_FILES = applications/mimeinfo.cache

239
gio/tests/filter-streams.c Normal file
View File

@ -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();
}