diff --git a/gio/goutputstream.c b/gio/goutputstream.c index e7b116736..dc15ff8cd 100644 --- a/gio/goutputstream.c +++ b/gio/goutputstream.c @@ -865,6 +865,189 @@ g_output_stream_write_finish (GOutputStream *stream, return g_task_propagate_int (G_TASK (result), error); } +typedef struct +{ + const guint8 *buffer; + gsize to_write; + gsize bytes_written; +} AsyncWriteAll; + +static void +free_async_write_all (gpointer data) +{ + g_slice_free (AsyncWriteAll, data); +} + +static void +write_all_callback (GObject *stream, + GAsyncResult *result, + gpointer user_data) +{ + GTask *task = user_data; + AsyncWriteAll *data = g_task_get_task_data (task); + + if (result) + { + GError *error = NULL; + gssize nwritten; + + nwritten = g_output_stream_write_finish (G_OUTPUT_STREAM (stream), result, &error); + + if (nwritten == -1) + { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + g_assert_cmpint (nwritten, <=, data->to_write); + g_warn_if_fail (nwritten > 0); + + data->to_write -= nwritten; + data->bytes_written += nwritten; + } + + if (data->to_write == 0) + { + g_task_return_boolean (task, TRUE); + g_object_unref (task); + } + + else + g_output_stream_write_async (G_OUTPUT_STREAM (stream), + data->buffer + data->bytes_written, + data->to_write, + g_task_get_priority (task), + g_task_get_cancellable (task), + write_all_callback, task); +} + +static void +write_all_async_thread (GTask *task, + gpointer source_object, + gpointer task_data, + GCancellable *cancellable) +{ + GOutputStream *stream = source_object; + AsyncWriteAll *data = task_data; + GError *error = NULL; + + if (g_output_stream_write_all (stream, data->buffer, data->to_write, &data->bytes_written, + g_task_get_cancellable (task), &error)) + g_task_return_boolean (task, TRUE); + else + g_task_return_error (task, error); +} + +/** + * g_output_stream_write_all_async: + * @stream: A #GOutputStream + * @buffer: (array length=count) (element-type guint8): the buffer containing the data to write + * @count: the number of bytes to write + * @io_priority: the io priority of the request + * @cancellable: (allow-none): optional #GCancellable object, %NULL to ignore + * @callback: (scope async): callback to call when the request is satisfied + * @user_data: (closure): the data to pass to callback function + * + * Request an asynchronous write of @count bytes from @buffer into + * the stream. When the operation is finished @callback will be called. + * You can then call g_output_stream_write_all_finish() to get the result of the + * operation. + * + * This is the asynchronous version of g_output_stream_write_all(). + * + * Call g_output_stream_write_all_finish() to collect the result. + * + * Any outstanding I/O request with higher priority (lower numerical + * value) will be executed before an outstanding request with lower + * priority. Default priority is %G_PRIORITY_DEFAULT. + * + * Note that no copy of @buffer will be made, so it must stay valid + * until @callback is called. + * + * Since: 2.44 + */ +void +g_output_stream_write_all_async (GOutputStream *stream, + const void *buffer, + gsize count, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + AsyncWriteAll *data; + GTask *task; + + g_return_if_fail (G_IS_OUTPUT_STREAM (stream)); + g_return_if_fail (buffer != NULL || count == 0); + + task = g_task_new (stream, cancellable, callback, user_data); + data = g_slice_new0 (AsyncWriteAll); + data->buffer = buffer; + data->to_write = count; + + g_task_set_task_data (task, data, free_async_write_all); + g_task_set_priority (task, io_priority); + + /* If async writes are going to be handled via the threadpool anyway + * then we may as well do it with a single dispatch instead of + * bouncing in and out. + */ + if (g_output_stream_async_write_is_via_threads (stream)) + { + g_task_run_in_thread (task, write_all_async_thread); + g_object_unref (task); + } + else + write_all_callback (G_OBJECT (stream), NULL, task); +} + +/** + * g_output_stream_write_all_finish: + * @stream: a #GOutputStream + * @result: a #GAsyncResult + * @bytes_written: (out): location to store the number of bytes that was written to the stream + * @error: a #GError location to store the error occurring, or %NULL to ignore. + * + * Finishes an asynchronous stream write operation started with + * g_output_stream_write_all_async(). + * + * As a special exception to the normal conventions for functions that + * use #GError, if this function returns %FALSE (and sets @error) then + * @bytes_written will be set to the number of bytes that were + * successfully written before the error was encountered. This + * functionality is only available from C. If you need it from another + * language then you must write your own loop around + * g_output_stream_write_async(). + * + * Returns: %TRUE on success, %FALSE if there was an error + * + * Since: 2.44 + **/ +gboolean +g_output_stream_write_all_finish (GOutputStream *stream, + GAsyncResult *result, + gsize *bytes_written, + GError **error) +{ + GTask *task; + + g_return_val_if_fail (G_IS_OUTPUT_STREAM (stream), FALSE); + g_return_val_if_fail (g_task_is_valid (result, stream), FALSE); + + task = G_TASK (result); + + if (bytes_written) + { + AsyncWriteAll *data = (AsyncWriteAll *)g_task_get_task_data (task); + + *bytes_written = data->bytes_written; + } + + return g_task_propagate_boolean (task, error); +} + static void write_bytes_callback (GObject *stream, GAsyncResult *result, diff --git a/gio/goutputstream.h b/gio/goutputstream.h index a5307409b..bc42abd0a 100644 --- a/gio/goutputstream.h +++ b/gio/goutputstream.h @@ -192,6 +192,22 @@ GLIB_AVAILABLE_IN_ALL gssize g_output_stream_write_finish (GOutputStream *stream, GAsyncResult *result, GError **error); + +GLIB_AVAILABLE_IN_2_44 +void g_output_stream_write_all_async (GOutputStream *stream, + const void *buffer, + gsize count, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +GLIB_AVAILABLE_IN_2_44 +gboolean g_output_stream_write_all_finish (GOutputStream *stream, + GAsyncResult *result, + gsize *bytes_written, + GError **error); + GLIB_AVAILABLE_IN_2_34 void g_output_stream_write_bytes_async (GOutputStream *stream, GBytes *bytes,