From eeb2bcf5a923b0521a69f525ff9c5243e2d3b45f Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 3 Feb 2022 12:20:36 +0100 Subject: [PATCH] Implement async file movement --- docs/reference/gio/gio-sections-common.txt | 2 + gio/gfile.c | 218 +++++++++++++++++++++ gio/gfile.h | 32 ++- 3 files changed, 248 insertions(+), 4 deletions(-) diff --git a/docs/reference/gio/gio-sections-common.txt b/docs/reference/gio/gio-sections-common.txt index 9f5d3d332..4842deaf7 100644 --- a/docs/reference/gio/gio-sections-common.txt +++ b/docs/reference/gio/gio-sections-common.txt @@ -149,6 +149,8 @@ g_file_copy g_file_copy_async g_file_copy_finish g_file_move +g_file_move_async +g_file_move_finish g_file_make_directory g_file_make_directory_async g_file_make_directory_finish diff --git a/gio/gfile.c b/gio/gfile.c index 6208c2f81..30daad3b6 100644 --- a/gio/gfile.c +++ b/gio/gfile.c @@ -247,6 +247,18 @@ static void g_file_real_trash_async (GFile static gboolean g_file_real_trash_finish (GFile *file, GAsyncResult *res, GError **error); +static void g_file_real_move_async (GFile *source, + GFile *destination, + GFileCopyFlags flags, + int io_priority, + GCancellable *cancellable, + GFileProgressCallback progress_callback, + gpointer progress_callback_data, + GAsyncReadyCallback callback, + gpointer user_data); +static gboolean g_file_real_move_finish (GFile *file, + GAsyncResult *result, + GError **error); static void g_file_real_make_directory_async (GFile *file, int io_priority, GCancellable *cancellable, @@ -381,6 +393,8 @@ g_file_default_init (GFileIface *iface) iface->delete_file_finish = g_file_real_delete_finish; iface->trash_async = g_file_real_trash_async; iface->trash_finish = g_file_real_trash_finish; + iface->move_async = g_file_real_move_async; + iface->move_finish = g_file_real_move_finish; iface->make_directory_async = g_file_real_make_directory_async; iface->make_directory_finish = g_file_real_make_directory_finish; iface->open_readwrite_async = g_file_real_open_readwrite_async; @@ -3770,6 +3784,91 @@ g_file_move (GFile *source, return g_file_delete (source, cancellable, error); } +/** + * g_file_move_async: + * @source: #GFile pointing to the source location + * @destination: #GFile pointing to the destination location + * @flags: set of #GFileCopyFlags + * @io_priority: the [I/O priority][io-priority] of the request + * @cancellable: (nullable): optional #GCancellable object, + * %NULL to ignore + * @progress_callback: (nullable) (scope call): #GFileProgressCallback + * function for updates + * @progress_callback_data: (closure): gpointer to user data for + * the callback function + * @callback: a #GAsyncReadyCallback to call + * when the request is satisfied + * @user_data: the data to pass to callback function + * + * Asynchronously moves a file @source to the location of @destination. For details of the behaviour, see g_file_move(). + * + * If @progress_callback is not %NULL, then that function that will be called + * just like in g_file_move(). The callback will run in the default main context + * of the thread calling g_file_move_async() — the same context as @callback is + * run in. + * + * When the operation is finished, @callback will be called. You can then call + * g_file_move_finish() to get the result of the operation. + * + * Since: 2.72 + */ +void +g_file_move_async (GFile *source, + GFile *destination, + GFileCopyFlags flags, + int io_priority, + GCancellable *cancellable, + GFileProgressCallback progress_callback, + gpointer progress_callback_data, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GFileIface *iface; + + g_return_if_fail (G_IS_FILE (source)); + g_return_if_fail (G_IS_FILE (destination)); + g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + + iface = G_FILE_GET_IFACE (source); + (* iface->move_async) (source, + destination, + flags, + io_priority, + cancellable, + progress_callback, + progress_callback_data, + callback, + user_data); +} + +/** + * g_file_move_finish: + * @file: input source #GFile + * @result: a #GAsyncResult + * @error: a #GError, or %NULL + * + * Finishes an asynchronous file movement, started with + * g_file_move_async(). + * + * Returns: %TRUE on successful file move, %FALSE otherwise. + * + * Since: 2.72 + */ +gboolean +g_file_move_finish (GFile *file, + GAsyncResult *result, + GError **error) +{ + GFileIface *iface; + + g_return_val_if_fail (G_IS_FILE (file), FALSE); + g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + iface = G_FILE_GET_IFACE (file); + return (* iface->move_finish) (file, result, error); +} + /** * g_file_make_directory: * @file: input #GFile @@ -6004,6 +6103,125 @@ g_file_real_trash_finish (GFile *file, return g_task_propagate_boolean (G_TASK (res), error); } + +typedef struct { + GFile *source; /* (owned) */ + GFile *destination; /* (owned) */ + GFileCopyFlags flags; + GFileProgressCallback progress_cb; + gpointer progress_cb_data; +} MoveAsyncData; + +static void +move_async_data_free (MoveAsyncData *data) +{ + g_object_unref (data->source); + g_object_unref (data->destination); + g_slice_free (MoveAsyncData, data); +} + +typedef struct { + MoveAsyncData *data; /* (unowned) */ + goffset current_num_bytes; + goffset total_num_bytes; +} MoveProgressData; + +static gboolean +move_async_progress_in_main (gpointer user_data) +{ + MoveProgressData *progress = user_data; + MoveAsyncData *data = progress->data; + + data->progress_cb (progress->current_num_bytes, + progress->total_num_bytes, + data->progress_cb_data); + + return G_SOURCE_REMOVE; +} + +static void +move_async_progress_callback (goffset current_num_bytes, + goffset total_num_bytes, + gpointer user_data) +{ + GTask *task = user_data; + MoveAsyncData *data = g_task_get_task_data (task); + MoveProgressData *progress; + + progress = g_new0 (MoveProgressData, 1); + progress->data = data; + progress->current_num_bytes = current_num_bytes; + progress->total_num_bytes = total_num_bytes; + + g_main_context_invoke_full (g_task_get_context (task), + g_task_get_priority (task), + move_async_progress_in_main, + g_steal_pointer (&progress), + g_free); +} + +static void +move_async_thread (GTask *task, + gpointer source, + gpointer task_data, + GCancellable *cancellable) +{ + MoveAsyncData *data = task_data; + gboolean result; + GError *error = NULL; + + result = g_file_move (data->source, + data->destination, + data->flags, + cancellable, + (data->progress_cb != NULL) ? move_async_progress_callback : NULL, + task, + &error); + if (result) + g_task_return_boolean (task, TRUE); + else + g_task_return_error (task, g_steal_pointer (&error)); +} + +static void +g_file_real_move_async (GFile *source, + GFile *destination, + GFileCopyFlags flags, + int io_priority, + GCancellable *cancellable, + GFileProgressCallback progress_callback, + gpointer progress_callback_data, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTask *task; + MoveAsyncData *data; + + data = g_slice_new0 (MoveAsyncData); + data->source = g_object_ref (source); + data->destination = g_object_ref (destination); + data->flags = flags; + data->progress_cb = progress_callback; + data->progress_cb_data = progress_callback_data; + + task = g_task_new (source, cancellable, callback, user_data); + g_task_set_source_tag (task, g_file_real_move_async); + g_task_set_task_data (task, g_steal_pointer (&data), (GDestroyNotify) move_async_data_free); + g_task_set_priority (task, io_priority); + g_task_run_in_thread (task, move_async_thread); + g_object_unref (task); +} + +static gboolean +g_file_real_move_finish (GFile *file, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (g_task_is_valid (result, file), FALSE); + + return g_task_propagate_boolean (G_TASK (result), error); +} + static void make_directory_async_thread (GTask *task, gpointer object, diff --git a/gio/gfile.h b/gio/gfile.h index 4cff1a372..3a324cf9d 100644 --- a/gio/gfile.h +++ b/gio/gfile.h @@ -121,8 +121,8 @@ typedef struct _GFileIface GFileIface; * @copy_async: Asynchronously copies a file. * @copy_finish: Finishes an asynchronous copy operation. * @move: Moves a file. - * @_move_async: Asynchronously moves a file. - * @_move_finish: Finishes an asynchronous move operation. + * @move_async: Asynchronously moves a file. Since: 2.72 + * @move_finish: Finishes an asynchronous move operation. Since: 2.72 * @mount_mountable: Mounts a mountable object. * @mount_mountable_finish: Finishes a mounting operation. * @unmount_mountable: Unmounts a mountable object. @@ -424,8 +424,18 @@ struct _GFileIface GFileProgressCallback progress_callback, gpointer progress_callback_data, GError **error); - void (* _move_async) (void); - void (* _move_finish) (void); + void (* move_async) (GFile *source, + GFile *destination, + GFileCopyFlags flags, + int io_priority, + GCancellable *cancellable, + GFileProgressCallback progress_callback, + gpointer progress_callback_data, + GAsyncReadyCallback callback, + gpointer user_data); + gboolean (* move_finish) (GFile *file, + GAsyncResult *result, + GError **error); void (* mount_mountable) (GFile *file, GMountMountFlags flags, @@ -926,6 +936,20 @@ gboolean g_file_move (GFile GFileProgressCallback progress_callback, gpointer progress_callback_data, GError **error); +GLIB_AVAILABLE_IN_2_72 +void g_file_move_async (GFile *source, + GFile *destination, + GFileCopyFlags flags, + int io_priority, + GCancellable *cancellable, + GFileProgressCallback progress_callback, + gpointer progress_callback_data, + GAsyncReadyCallback callback, + gpointer user_data); +GLIB_AVAILABLE_IN_2_72 +gboolean g_file_move_finish (GFile *file, + GAsyncResult *result, + GError **error); GLIB_AVAILABLE_IN_ALL gboolean g_file_make_directory (GFile *file, GCancellable *cancellable,