X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=gio%2Fgiostream.c;h=cd177acf0faa0a040c886fdf1728fdb13eb3a956;hb=4482977238ae80f64c2fe318d1500f4662c73980;hp=a79e09951e57611963ed291babd530be605571e8;hpb=c20b8d4d53a4e90f0e822276f6fbd94d52ff3c85;p=platform%2Fupstream%2Fglib.git diff --git a/gio/giostream.c b/gio/giostream.c index a79e099..cd177ac 100644 --- a/gio/giostream.c +++ b/gio/giostream.c @@ -14,9 +14,7 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General - * Public License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place, Suite 330, - * Boston, MA 02111-1307, USA. + * Public License along with this library; if not, see . * * Authors: Ryan Lortie * Alexander Larsson @@ -27,12 +25,8 @@ #include "glibintl.h" #include "giostream.h" -#include -#include - -#include "gioalias.h" - -G_DEFINE_TYPE (GIOStream, g_io_stream, G_TYPE_OBJECT); +#include "gasyncresult.h" +#include "gtask.h" /** * SECTION:giostream @@ -61,12 +55,12 @@ G_DEFINE_TYPE (GIOStream, g_io_stream, G_TYPE_OBJECT); * To close a stream use g_io_stream_close() which will close the common * stream object and also the individual substreams. You can also close * the substreams themselves. In most cases this only marks the - * substream as closed, so further I/O on it fails. However, some streams - * may support "half-closed" states where one direction of the stream - * is actually shut down. + * substream as closed, so further I/O on it fails but common state in the + * #GIOStream may still be open. However, some streams may support + * "half-closed" states where one direction of the stream is actually shut down. * * Since: 2.22 - **/ + */ enum { @@ -79,7 +73,6 @@ enum struct _GIOStreamPrivate { guint closed : 1; guint pending : 1; - GAsyncReadyCallback outstanding_callback; }; static gboolean g_io_stream_real_close (GIOStream *stream, @@ -94,15 +87,7 @@ static gboolean g_io_stream_real_close_finish (GIOStream *stream, GAsyncResult *result, GError **error); -static void -g_io_stream_finalize (GObject *object) -{ - GIOStream *stream; - - stream = G_IO_STREAM (object); - - G_OBJECT_CLASS (g_io_stream_parent_class)->finalize (object); -} +G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GIOStream, g_io_stream, G_TYPE_OBJECT) static void g_io_stream_dispose (GObject *object) @@ -120,9 +105,7 @@ g_io_stream_dispose (GObject *object) static void g_io_stream_init (GIOStream *stream) { - stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (stream, - G_TYPE_IO_STREAM, - GIOStreamPrivate); + stream->priv = g_io_stream_get_instance_private (stream); } static void @@ -153,28 +136,11 @@ g_io_stream_get_property (GObject *object, } static void -g_io_stream_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - switch (prop_id) - { - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void g_io_stream_class_init (GIOStreamClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - g_type_class_add_private (klass, sizeof (GIOStreamPrivate)); - - gobject_class->finalize = g_io_stream_finalize; gobject_class->dispose = g_io_stream_dispose; - gobject_class->set_property = g_io_stream_set_property; gobject_class->get_property = g_io_stream_get_property; klass->close_fn = g_io_stream_real_close; @@ -186,7 +152,7 @@ g_io_stream_class_init (GIOStreamClass *klass) P_("Closed"), P_("Is the stream closed"), FALSE, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_INPUT_STREAM, g_param_spec_object ("input-stream", @@ -204,14 +170,14 @@ g_io_stream_class_init (GIOStreamClass *klass) /** * g_io_stream_is_closed: - * @stream: a #GIOStream. + * @stream: a #GIOStream * * Checks if a stream is closed. * * Returns: %TRUE if the stream is closed. * * Since: 2.22 - **/ + */ gboolean g_io_stream_is_closed (GIOStream *stream) { @@ -222,56 +188,58 @@ g_io_stream_is_closed (GIOStream *stream) /** * g_io_stream_get_input_stream: - * @stream: input #GIOStream. + * @stream: a #GIOStream * * Gets the input stream for this object. This is used * for reading. * - * Returns: a #GInputStream, owned by the #GIOStream do not free. + * Returns: (transfer none): a #GInputStream, owned by the #GIOStream. + * Do not free. * * Since: 2.22 - **/ + */ GInputStream * -g_io_stream_get_input_stream (GIOStream *io_stream) +g_io_stream_get_input_stream (GIOStream *stream) { GIOStreamClass *klass; - klass = G_IO_STREAM_GET_CLASS (io_stream); + klass = G_IO_STREAM_GET_CLASS (stream); g_assert (klass->get_input_stream != NULL); - return klass->get_input_stream (io_stream); + return klass->get_input_stream (stream); } /** * g_io_stream_get_output_stream: - * @stream: input #GIOStream. + * @stream: a #GIOStream * * Gets the output stream for this object. This is used for * writing. * - * Returns: a #GOutputStream, owned by the #GIOStream do not free. + * Returns: (transfer none): a #GOutputStream, owned by the #GIOStream. + * Do not free. * * Since: 2.22 - **/ + */ GOutputStream * -g_io_stream_get_output_stream (GIOStream *io_stream) +g_io_stream_get_output_stream (GIOStream *stream) { GIOStreamClass *klass; - klass = G_IO_STREAM_GET_CLASS (io_stream); + klass = G_IO_STREAM_GET_CLASS (stream); g_assert (klass->get_output_stream != NULL); - return klass->get_output_stream (io_stream); + return klass->get_output_stream (stream); } /** * g_io_stream_has_pending: - * @stream: a #GIOStream. + * @stream: a #GIOStream * * Checks if a stream has pending actions. * - * Returns: %TRUE if @stream has pending actions. + * Returns: %TRUE if @stream has pending actions. * * Since: 2.22 **/ @@ -285,21 +253,21 @@ g_io_stream_has_pending (GIOStream *stream) /** * g_io_stream_set_pending: - * @stream: a #GIOStream. - * @error: a #GError location to store the error occuring, or %NULL to - * ignore. + * @stream: a #GIOStream + * @error: a #GError location to store the error occurring, or %NULL to + * ignore * * Sets @stream to have actions pending. If the pending flag is * already set or @stream is closed, it will return %FALSE and set * @error. * - * Return value: %TRUE if pending was previously unset and is now set. + * Returns: %TRUE if pending was previously unset and is now set. * * Since: 2.22 - **/ + */ gboolean -g_io_stream_set_pending (GIOStream *stream, - GError **error) +g_io_stream_set_pending (GIOStream *stream, + GError **error) { g_return_val_if_fail (G_IS_IO_STREAM (stream), FALSE); @@ -326,12 +294,12 @@ g_io_stream_set_pending (GIOStream *stream, /** * g_io_stream_clear_pending: - * @stream: output stream + * @stream: a #GIOStream * * Clears the pending flag on @stream. * * Since: 2.22 - **/ + */ void g_io_stream_clear_pending (GIOStream *stream) { @@ -341,9 +309,9 @@ g_io_stream_clear_pending (GIOStream *stream) } static gboolean -g_io_stream_real_close (GIOStream *stream, - GCancellable *cancellable, - GError **error) +g_io_stream_real_close (GIOStream *stream, + GCancellable *cancellable, + GError **error) { gboolean res; @@ -363,33 +331,34 @@ g_io_stream_real_close (GIOStream *stream, /** * g_io_stream_close: - * @stream: A #GIOStream. - * @cancellable: optional #GCancellable object, %NULL to ignore. - * @error: location to store the error occuring, or %NULL to ignore + * @stream: a #GIOStream + * @cancellable: (allow-none): optional #GCancellable object, %NULL to ignore + * @error: location to store the error occurring, or %NULL to ignore * * Closes the stream, releasing resources related to it. This will also * closes the individual input and output streams, if they are not already * closed. * - * Once the stream is closed, all other operations will return %G_IO_ERROR_CLOSED. - * Closing a stream multiple times will not return an error. + * Once the stream is closed, all other operations will return + * %G_IO_ERROR_CLOSED. Closing a stream multiple times will not + * return an error. * - * Closing a stream will automatically flush any outstanding buffers in the - * stream. + * Closing a stream will automatically flush any outstanding buffers + * in the stream. * * Streams will be automatically closed when the last reference * is dropped, but you might want to call this function to make sure * resources are released as early as possible. * - * Some streams might keep the backing store of the stream (e.g. a file descriptor) - * open after the stream is closed. See the documentation for the individual - * stream for details. + * Some streams might keep the backing store of the stream (e.g. a file + * descriptor) open after the stream is closed. See the documentation for + * the individual stream for details. * - * On failure the first error that happened will be reported, but the close - * operation will finish as much as possible. A stream that failed to - * close will still return %G_IO_ERROR_CLOSED for all operations. Still, it - * is important to check and report the error to the user, otherwise - * there might be a loss of data as all data might not be written. + * On failure the first error that happened will be reported, but the + * close operation will finish as much as possible. A stream that failed + * to close will still return %G_IO_ERROR_CLOSED for all operations. + * Still, it is important to check and report the error to the user, + * otherwise there might be a loss of data as all data might not be written. * * If @cancellable is not NULL, then the operation can be cancelled by * triggering the cancellable object from another thread. If the operation @@ -400,12 +369,12 @@ g_io_stream_real_close (GIOStream *stream, * The default implementation of this method just calls close on the * individual input/output streams. * - * Return value: %TRUE on success, %FALSE on failure + * Returns: %TRUE on success, %FALSE on failure * * Since: 2.22 - **/ + */ gboolean -g_io_stream_close (GIOStream *stream, +g_io_stream_close (GIOStream *stream, GCancellable *cancellable, GError **error) { @@ -444,21 +413,34 @@ async_ready_close_callback_wrapper (GObject *source_object, gpointer user_data) { GIOStream *stream = G_IO_STREAM (source_object); + GIOStreamClass *klass = G_IO_STREAM_GET_CLASS (stream); + GTask *task = user_data; + GError *error = NULL; + gboolean success; stream->priv->closed = TRUE; g_io_stream_clear_pending (stream); - if (stream->priv->outstanding_callback) - (*stream->priv->outstanding_callback) (source_object, res, user_data); - g_object_unref (stream); + + if (g_async_result_legacy_propagate_error (res, &error)) + success = FALSE; + else + success = klass->close_finish (stream, res, &error); + + if (error) + g_task_return_error (task, error); + else + g_task_return_boolean (task, success); + + g_object_unref (task); } /** * g_io_stream_close_async: - * @stream: A #GIOStream. - * @io_priority: the io priority of the request. - * @callback: callback to call when the request is satisfied - * @user_data: the data to pass to callback function - * @cancellable: optional cancellable object + * @stream: a #GIOStream + * @io_priority: the io priority of the request + * @cancellable: (allow-none): optional cancellable object + * @callback: (scope async): callback to call when the request is satisfied + * @user_data: (closure): the data to pass to callback function * * Requests an asynchronous close of the stream, releasing resources * related to it. When the operation is finished @callback will be @@ -467,12 +449,12 @@ async_ready_close_callback_wrapper (GObject *source_object, * * For behaviour details see g_io_stream_close(). * - * The asyncronous methods have a default fallback that uses threads + * The asynchronous methods have a default fallback that uses threads * to implement asynchronicity, so they are optional for inheriting * classes. However, if you override one you must override all. * * Since: 2.22 - **/ + */ void g_io_stream_close_async (GIOStream *stream, int io_priority, @@ -481,138 +463,358 @@ g_io_stream_close_async (GIOStream *stream, gpointer user_data) { GIOStreamClass *class; - GSimpleAsyncResult *simple; GError *error = NULL; + GTask *task; g_return_if_fail (G_IS_IO_STREAM (stream)); + task = g_task_new (stream, cancellable, callback, user_data); + if (stream->priv->closed) { - simple = g_simple_async_result_new (G_OBJECT (stream), - callback, - user_data, - g_io_stream_close_async); - g_simple_async_result_complete_in_idle (simple); - g_object_unref (simple); + g_task_return_boolean (task, TRUE); + g_object_unref (task); return; } if (!g_io_stream_set_pending (stream, &error)) { - g_simple_async_report_gerror_in_idle (G_OBJECT (stream), - callback, - user_data, - error); - g_error_free (error); + g_task_return_error (task, error); + g_object_unref (task); return; } class = G_IO_STREAM_GET_CLASS (stream); - stream->priv->outstanding_callback = callback; - g_object_ref (stream); + class->close_async (stream, io_priority, cancellable, - async_ready_close_callback_wrapper, user_data); + async_ready_close_callback_wrapper, task); } /** * g_io_stream_close_finish: - * @stream: a #GIOStream. - * @result: a #GAsyncResult. - * @error: a #GError location to store the error occuring, or %NULL to - * ignore. + * @stream: a #GIOStream + * @result: a #GAsyncResult + * @error: a #GError location to store the error occurring, or %NULL to + * ignore * * Closes a stream. * * Returns: %TRUE if stream was successfully closed, %FALSE otherwise. * * Since: 2.22 - **/ + */ gboolean -g_io_stream_close_finish (GIOStream *stream, - GAsyncResult *result, - GError **error) +g_io_stream_close_finish (GIOStream *stream, + GAsyncResult *result, + GError **error) { - GSimpleAsyncResult *simple; - GIOStreamClass *class; - g_return_val_if_fail (G_IS_IO_STREAM (stream), FALSE); - g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE); + g_return_val_if_fail (g_task_is_valid (result, stream), FALSE); - if (G_IS_SIMPLE_ASYNC_RESULT (result)) - { - simple = G_SIMPLE_ASYNC_RESULT (result); - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - - /* Special case already closed */ - if (g_simple_async_result_get_source_tag (simple) == g_io_stream_close_async) - return TRUE; - } - - class = G_IO_STREAM_GET_CLASS (stream); - return class->close_finish (stream, result, error); + return g_task_propagate_boolean (G_TASK (result), error); } static void -close_async_thread (GSimpleAsyncResult *res, - GObject *object, - GCancellable *cancellable) +close_async_thread (GTask *task, + gpointer source_object, + gpointer task_data, + GCancellable *cancellable) { + GIOStream *stream = source_object; GIOStreamClass *class; GError *error = NULL; gboolean result; - /* Auto handling of cancelation disabled, and ignore - cancellation, since we want to close things anyway, although - possibly in a quick-n-dirty way. At least we never want to leak - open handles */ - - class = G_IO_STREAM_GET_CLASS (object); + class = G_IO_STREAM_GET_CLASS (stream); if (class->close_fn) { - result = class->close_fn (G_IO_STREAM (object), cancellable, &error); + result = class->close_fn (stream, + g_task_get_cancellable (task), + &error); if (!result) + { + 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) { - g_simple_async_result_set_from_error (res, error); - g_error_free (error); + g_io_stream_close_async (ctx->stream1, + g_task_get_priority (task), + ctx->op1_cancellable, + splice_close_cb, g_object_ref (task)); } + else + ctx->completed++; + + if ((ctx->flags & G_IO_STREAM_SPLICE_CLOSE_STREAM2) != 0) + { + g_io_stream_close_async (ctx->stream2, + g_task_get_priority (task), + ctx->op2_cancellable, + splice_close_cb, g_object_ref (task)); + } + else + ctx->completed++; + + /* If all operations are done, complete now */ + if (ctx->completed == 4) + splice_complete (task, ctx); } + + g_object_unref (task); } static void -g_io_stream_real_close_async (GIOStream *stream, - int io_priority, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) +splice_cancelled_cb (GCancellable *cancellable, + GTask *task) +{ + SpliceContext *ctx; + + ctx = g_task_get_task_data (task); + g_cancellable_cancel (ctx->op1_cancellable); + g_cancellable_cancel (ctx->op2_cancellable); +} + +/** + * g_io_stream_splice_async: + * @stream1: a #GIOStream. + * @stream2: a #GIOStream. + * @flags: a set of #GIOStreamSpliceFlags. + * @io_priority: the io priority of the request. + * @cancellable: (allow-none): optional #GCancellable object, %NULL to ignore. + * @callback: (scope async): a #GAsyncReadyCallback. + * @user_data: (closure): user data passed to @callback. + * + * Asyncronously splice the output stream of @stream1 to the input stream of + * @stream2, and splice the output stream of @stream2 to the input stream of + * @stream1. + * + * When the operation is finished @callback will be called. + * You can then call g_io_stream_splice_finish() to get the + * result of the operation. + * + * Since: 2.28 + **/ +void +g_io_stream_splice_async (GIOStream *stream1, + GIOStream *stream2, + GIOStreamSpliceFlags flags, + gint io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) { - GSimpleAsyncResult *res; + GTask *task; + SpliceContext *ctx; + GInputStream *istream; + GOutputStream *ostream; - res = g_simple_async_result_new (G_OBJECT (stream), - callback, - user_data, - g_io_stream_real_close_async); + if (cancellable != NULL && g_cancellable_is_cancelled (cancellable)) + { + g_task_report_new_error (NULL, callback, user_data, + g_io_stream_splice_async, + G_IO_ERROR, G_IO_ERROR_CANCELLED, + "Operation has been cancelled"); + return; + } + + ctx = g_slice_new0 (SpliceContext); + ctx->stream1 = g_object_ref (stream1); + ctx->stream2 = g_object_ref (stream2); + ctx->flags = flags; + ctx->op1_cancellable = g_cancellable_new (); + ctx->op2_cancellable = g_cancellable_new (); + ctx->completed = 0; - g_simple_async_result_set_handle_cancellation (res, FALSE); + task = g_task_new (NULL, cancellable, callback, user_data); + g_task_set_task_data (task, ctx, (GDestroyNotify) splice_context_free); - g_simple_async_result_run_in_thread (res, - close_async_thread, - io_priority, - cancellable); - g_object_unref (res); + if (cancellable != NULL) + { + ctx->cancellable = g_object_ref (cancellable); + ctx->cancelled_id = g_cancellable_connect (cancellable, + G_CALLBACK (splice_cancelled_cb), g_object_ref (task), + g_object_unref); + } + + istream = g_io_stream_get_input_stream (stream1); + ostream = g_io_stream_get_output_stream (stream2); + g_output_stream_splice_async (ostream, istream, G_OUTPUT_STREAM_SPLICE_NONE, + io_priority, ctx->op1_cancellable, splice_cb, + g_object_ref (task)); + + istream = g_io_stream_get_input_stream (stream2); + ostream = g_io_stream_get_output_stream (stream1); + g_output_stream_splice_async (ostream, istream, G_OUTPUT_STREAM_SPLICE_NONE, + io_priority, ctx->op2_cancellable, splice_cb, + g_object_ref (task)); + + g_object_unref (task); } -static gboolean -g_io_stream_real_close_finish (GIOStream *stream, - GAsyncResult *result, - GError **error) +/** + * g_io_stream_splice_finish: + * @result: a #GAsyncResult. + * @error: a #GError location to store the error occurring, or %NULL to + * ignore. + * + * Finishes an asynchronous io stream splice operation. + * + * Returns: %TRUE on success, %FALSE otherwise. + * + * Since: 2.28 + **/ +gboolean +g_io_stream_splice_finish (GAsyncResult *result, + GError **error) { - GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result); - g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == - g_io_stream_real_close_async); - return TRUE; -} + g_return_val_if_fail (g_task_is_valid (result, NULL), FALSE); -#define __G_IO_STREAM_C__ -#include "gioaliasdef.c" + return g_task_propagate_boolean (G_TASK (result), error); +}