Merge branch 'copy-move-async-with-closures' into 'main'

Add g_file_copy_async_with_closures() and g_file_move_async_with_closures()

See merge request GNOME/glib!3939
This commit is contained in:
Philip Withnall 2024-03-22 00:55:07 +00:00
commit 71d60faef3
3 changed files with 358 additions and 0 deletions

View File

@ -3818,6 +3818,123 @@ g_file_copy_async (GFile *source,
user_data);
}
typedef struct _CopyAsyncClosuresData
{
GClosure *progress_callback_closure;
GClosure *ready_callback_closure;
} CopyAsyncClosuresData;
static CopyAsyncClosuresData *
copy_async_closures_data_new (GClosure *progress_callback_closure,
GClosure *ready_callback_closure)
{
CopyAsyncClosuresData *data;
data = g_new0 (CopyAsyncClosuresData, 1);
if (progress_callback_closure != NULL)
{
data->progress_callback_closure = g_closure_ref (progress_callback_closure);
g_closure_sink (progress_callback_closure);
if (G_CLOSURE_NEEDS_MARSHAL (progress_callback_closure))
g_closure_set_marshal (progress_callback_closure, g_cclosure_marshal_generic);
}
data->ready_callback_closure = g_closure_ref (ready_callback_closure);
g_closure_sink (ready_callback_closure);
if (G_CLOSURE_NEEDS_MARSHAL (ready_callback_closure))
g_closure_set_marshal (ready_callback_closure, g_cclosure_marshal_generic);
return data;
}
static void
copy_async_closures_data_free (CopyAsyncClosuresData *data)
{
if (data->progress_callback_closure != NULL)
g_closure_unref (data->progress_callback_closure);
g_closure_unref (data->ready_callback_closure);
g_free (data);
}
static void
copy_async_invoke_progress (goffset current_num_bytes,
goffset total_num_bytes,
void *user_data)
{
CopyAsyncClosuresData *data = (CopyAsyncClosuresData *) user_data;
GValue params[2] = { G_VALUE_INIT, G_VALUE_INIT };
/* goffset is 64-bits even on 32-bits platforms */
g_value_init (&params[0], G_TYPE_INT64);
g_value_set_int64 (&params[0], current_num_bytes);
g_value_init (&params[1], G_TYPE_INT64);
g_value_set_int64 (&params[1], total_num_bytes);
g_closure_invoke (data->progress_callback_closure, /* result = */ NULL, 2, params, /* hint = */ NULL);
g_value_unset (&params[0]);
g_value_unset (&params[1]);
}
static void
copy_async_invoke_ready (GObject *file,
GAsyncResult *result,
void *user_data)
{
CopyAsyncClosuresData *data = (CopyAsyncClosuresData *) user_data;
GValue params[2] = { G_VALUE_INIT, G_VALUE_INIT };
g_value_init (&params[0], G_TYPE_FILE);
g_value_set_object (&params[0], file);
g_value_init (&params[1], G_TYPE_ASYNC_RESULT);
g_value_set_object (&params[1], result);
g_closure_invoke (data->ready_callback_closure, /* result = */ NULL, 2, params, /* hint = */ NULL);
copy_async_closures_data_free (data);
g_value_unset (&params[0]);
g_value_unset (&params[1]);
}
/**
* g_file_copy_async_with_closures: (rename-to g_file_copy_async) (finish-func g_file_copy_finish):
* @source: input [type@Gio.File]
* @destination: destination [type@Gio.File]
* @flags: set of [flags@Gio.FileCopyFlags]
* @io_priority: the [I/O priority](iface.AsyncResult.html#io-priority) of the request
* @cancellable: (nullable): optional [class@Gio.Cancellable] object,
* `NULL` to ignore
* @progress_callback_closure: (nullable): [type@GObject.Closure] to invoke with progress
* information, or `NULL` if progress information is not needed
* @ready_callback_closure: (not nullable): [type@GObject.Closure] to invoke when the request is satisfied
*
* Version of [method@Gio.File.copy_async] using closures instead of callbacks for
* easier binding in other languages.
*
* Since: 2.82
*/
void
g_file_copy_async_with_closures (GFile *source,
GFile *destination,
GFileCopyFlags flags,
int io_priority,
GCancellable *cancellable,
GClosure *progress_callback_closure,
GClosure *ready_callback_closure)
{
CopyAsyncClosuresData *data;
/* freed in copy_async_invoke_ready */
data = copy_async_closures_data_new (progress_callback_closure, ready_callback_closure);
g_file_copy_async (source, destination, flags, io_priority, cancellable,
progress_callback_closure == NULL ? NULL : copy_async_invoke_progress, data,
copy_async_invoke_ready, data);
}
/**
* g_file_copy_finish:
* @file: input #GFile
@ -4035,6 +4152,42 @@ g_file_move_async (GFile *source,
user_data);
}
/**
* g_file_move_async_with_closures: (rename-to g_file_move_async) (finish-func g_file_move_finish):
* @source: input [type@Gio.File]
* @destination: destination [type@Gio.File]
* @flags: set of [flags@Gio.FileCopyFlags]
* @io_priority: the [I/O priority](iface.AsyncResult.html#io-priority) of the request
* @cancellable: (nullable): optional [class@Gio.Cancellable] object,
* `NULL` to ignore
* @progress_callback_closure: (nullable): [type@GObject.Closure] to invoke with progress
* information, or `NULL` if progress information is not needed
* @ready_callback_closure: (not nullable): [type@GObject.Closure] to invoke when the request is satisfied
*
* Version of [method@Gio.File.move_async] using closures instead of callbacks for
* easier binding in other languages.
*
* Since: 2.82
*/
void
g_file_move_async_with_closures (GFile *source,
GFile *destination,
GFileCopyFlags flags,
int io_priority,
GCancellable *cancellable,
GClosure *progress_callback_closure,
GClosure *ready_callback_closure)
{
CopyAsyncClosuresData *data;
/* freed in copy_async_invoke_ready */
data = copy_async_closures_data_new (progress_callback_closure, ready_callback_closure);
g_file_move_async (source, destination, flags, io_priority, cancellable,
progress_callback_closure == NULL ? NULL : copy_async_invoke_progress, data,
copy_async_invoke_ready, data);
}
/**
* g_file_move_finish:
* @file: input source #GFile

View File

@ -944,6 +944,14 @@ void g_file_copy_async (GFile
gpointer progress_callback_data,
GAsyncReadyCallback callback,
gpointer user_data);
GIO_AVAILABLE_IN_2_82
void g_file_copy_async_with_closures (GFile *source,
GFile *destination,
GFileCopyFlags flags,
int io_priority,
GCancellable *cancellable,
GClosure *progress_callback_closure,
GClosure *ready_callback_closure);
GIO_AVAILABLE_IN_ALL
gboolean g_file_copy_finish (GFile *file,
GAsyncResult *res,
@ -966,6 +974,14 @@ void g_file_move_async (GFile
gpointer progress_callback_data,
GAsyncReadyCallback callback,
gpointer user_data);
GIO_AVAILABLE_IN_2_82
void g_file_move_async_with_closures (GFile *source,
GFile *destination,
GFileCopyFlags flags,
int io_priority,
GCancellable *cancellable,
GClosure *progress_callback_closure,
GClosure *ready_callback_closure);
GIO_AVAILABLE_IN_2_72
gboolean g_file_move_finish (GFile *file,
GAsyncResult *result,

View File

@ -2606,6 +2606,119 @@ test_copy_progress (void)
g_clear_object (&dest_tmpfile);
}
typedef struct
{
GError *error;
gboolean done;
gboolean res;
} CopyAsyncData;
static void
test_copy_async_cb (GObject *object,
GAsyncResult *result,
void *user_data)
{
GFile *file = G_FILE (object);
CopyAsyncData *data = user_data;
GError *error = NULL;
data->res = g_file_move_finish (file, result, &error);
data->error = g_steal_pointer (&error);
data->done = TRUE;
}
typedef struct
{
goffset total_num_bytes;
} CopyAsyncProgressData;
static void
test_copy_async_progress_cb (goffset current_num_bytes,
goffset total_num_bytes,
void *user_data)
{
CopyAsyncProgressData *data = user_data;
data->total_num_bytes = total_num_bytes;
}
/* Exercise copy_async_with_closures() */
static void
test_copy_async_with_closures (void)
{
CopyAsyncData data = { 0 };
CopyAsyncProgressData progress_data = { 0 };
GFile *source;
GFileIOStream *iostream;
GOutputStream *ostream;
GFile *destination;
gchar *destination_path;
GError *error = NULL;
gboolean res;
const guint8 buffer[] = { 1, 2, 3, 4, 5 };
GClosure *progress_closure;
GClosure *ready_closure;
source = g_file_new_tmp ("g_file_copy_async_with_closures_XXXXXX", &iostream, NULL);
destination_path = g_build_path (G_DIR_SEPARATOR_S, g_get_tmp_dir (), "g_file_copy_async_with_closures_target", NULL);
destination = g_file_new_for_path (destination_path);
g_assert_nonnull (source);
g_assert_nonnull (iostream);
res = g_file_query_exists (source, NULL);
g_assert_true (res);
res = g_file_query_exists (destination, NULL);
g_assert_false (res);
/* Write a known number of bytes to the file, so we can test the progress
* callback against it */
ostream = g_io_stream_get_output_stream (G_IO_STREAM (iostream));
g_output_stream_write (ostream, buffer, sizeof (buffer), NULL, &error);
g_assert_no_error (error);
progress_closure = g_cclosure_new (G_CALLBACK (test_copy_async_progress_cb), &progress_data, NULL);
ready_closure = g_cclosure_new (G_CALLBACK (test_copy_async_cb), &data, NULL);
g_file_copy_async_with_closures (source,
destination,
G_FILE_COPY_NONE,
0,
NULL,
progress_closure,
ready_closure);
while (!data.done)
g_main_context_iteration (NULL, TRUE);
g_assert_no_error (data.error);
g_assert_true (data.res);
g_assert_cmpuint (progress_data.total_num_bytes, ==, sizeof (buffer));
res = g_file_query_exists (source, NULL);
g_assert_true (res);
res = g_file_query_exists (destination, NULL);
g_assert_true (res);
res = g_io_stream_close (G_IO_STREAM (iostream), NULL, &error);
g_assert_no_error (error);
g_assert_true (res);
g_object_unref (iostream);
res = g_file_delete (source, NULL, &error);
g_assert_no_error (error);
g_assert_true (res);
res = g_file_delete (destination, NULL, &error);
g_assert_no_error (error);
g_assert_true (res);
g_object_unref (source);
g_object_unref (destination);
g_free (destination_path);
}
static void
test_measure (void)
{
@ -3577,6 +3690,80 @@ test_move_async (void)
g_free (destination_path);
}
/* Same test as for move_async(), but for move_async_with_closures() */
static void
test_move_async_with_closures (void)
{
MoveAsyncData data = { 0 };
MoveAsyncProgressData progress_data = { 0 };
GFile *source;
GFileIOStream *iostream;
GOutputStream *ostream;
GFile *destination;
gchar *destination_path;
GError *error = NULL;
gboolean res;
const guint8 buffer[] = { 1, 2, 3, 4, 5 };
GClosure *progress_closure;
GClosure *ready_closure;
source = g_file_new_tmp ("g_file_move_async_with_closures_XXXXXX", &iostream, NULL);
destination_path = g_build_path (G_DIR_SEPARATOR_S, g_get_tmp_dir (), "g_file_move_async_with_closures_target", NULL);
destination = g_file_new_for_path (destination_path);
g_assert_nonnull (source);
g_assert_nonnull (iostream);
res = g_file_query_exists (source, NULL);
g_assert_true (res);
res = g_file_query_exists (destination, NULL);
g_assert_false (res);
/* Write a known number of bytes to the file, so we can test the progress
* callback against it */
ostream = g_io_stream_get_output_stream (G_IO_STREAM (iostream));
g_output_stream_write (ostream, buffer, sizeof (buffer), NULL, &error);
g_assert_no_error (error);
progress_closure = g_cclosure_new (G_CALLBACK (test_move_async_progress_cb), &progress_data, NULL);
ready_closure = g_cclosure_new (G_CALLBACK (test_move_async_cb), &data, NULL);
g_file_move_async_with_closures (source,
destination,
G_FILE_COPY_NONE,
0,
NULL,
progress_closure,
ready_closure);
while (!data.done)
g_main_context_iteration (NULL, TRUE);
g_assert_no_error (data.error);
g_assert_true (data.res);
g_assert_cmpuint (progress_data.total_num_bytes, ==, sizeof (buffer));
res = g_file_query_exists (source, NULL);
g_assert_false (res);
res = g_file_query_exists (destination, NULL);
g_assert_true (res);
res = g_io_stream_close (G_IO_STREAM (iostream), NULL, &error);
g_assert_no_error (error);
g_assert_true (res);
g_object_unref (iostream);
res = g_file_delete (destination, NULL, &error);
g_assert_no_error (error);
g_assert_true (res);
g_object_unref (source);
g_object_unref (destination);
g_free (destination_path);
}
static GAppInfo *
create_command_line_app_info (const char *name,
const char *command_line,
@ -4028,6 +4215,7 @@ main (int argc, char *argv[])
g_test_add_func ("/file/async-make-symlink", test_async_make_symlink);
g_test_add_func ("/file/copy-preserve-mode", test_copy_preserve_mode);
g_test_add_func ("/file/copy/progress", test_copy_progress);
g_test_add_func ("/file/copy-async-with-closurse", test_copy_async_with_closures);
g_test_add_func ("/file/measure", test_measure);
g_test_add_func ("/file/measure-async", test_measure_async);
g_test_add_func ("/file/load-bytes", test_load_bytes);
@ -4045,6 +4233,7 @@ main (int argc, char *argv[])
g_test_add_func ("/file/writev/async_all-cancellation", test_writev_async_all_cancellation);
g_test_add_func ("/file/build-attribute-list-for-copy", test_build_attribute_list_for_copy);
g_test_add_func ("/file/move_async", test_move_async);
g_test_add_func ("/file/move-async-with-closures", test_move_async_with_closures);
g_test_add_func ("/file/query-zero-length-content-type", test_query_zero_length_content_type);
g_test_add_func ("/file/query-default-handler-file", test_query_default_handler_file);
g_test_add_func ("/file/query-default-handler-file-async", test_query_default_handler_file_async);