mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-11-09 19:06:15 +01:00
Merge branch 'feature/move_async' into 'main'
Implement async file movement See merge request GNOME/glib!2469
This commit is contained in:
commit
50d23f9680
@ -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
|
||||
|
226
gio/gfile.c
226
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;
|
||||
@ -3768,6 +3782,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
|
||||
@ -6002,6 +6101,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,
|
||||
@ -6401,12 +6619,12 @@ typedef struct {
|
||||
CopyAsyncData *data;
|
||||
goffset current_num_bytes;
|
||||
goffset total_num_bytes;
|
||||
} ProgressData;
|
||||
} CopyProgressData;
|
||||
|
||||
static gboolean
|
||||
copy_async_progress_in_main (gpointer user_data)
|
||||
{
|
||||
ProgressData *progress = user_data;
|
||||
CopyProgressData *progress = user_data;
|
||||
CopyAsyncData *data = progress->data;
|
||||
|
||||
data->progress_cb (progress->current_num_bytes,
|
||||
@ -6423,9 +6641,9 @@ copy_async_progress_callback (goffset current_num_bytes,
|
||||
{
|
||||
GTask *task = user_data;
|
||||
CopyAsyncData *data = g_task_get_task_data (task);
|
||||
ProgressData *progress;
|
||||
CopyProgressData *progress;
|
||||
|
||||
progress = g_new (ProgressData, 1);
|
||||
progress = g_new (CopyProgressData, 1);
|
||||
progress->data = data;
|
||||
progress->current_num_bytes = current_num_bytes;
|
||||
progress->total_num_bytes = total_num_bytes;
|
||||
|
32
gio/gfile.h
32
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,
|
||||
|
106
gio/tests/file.c
106
gio/tests/file.c
@ -3006,6 +3006,111 @@ test_build_attribute_list_for_copy (void)
|
||||
g_clear_object (&tmpfile);
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GError *error;
|
||||
gboolean done;
|
||||
gboolean res;
|
||||
} MoveAsyncData;
|
||||
|
||||
static void
|
||||
test_move_async_cb (GObject *object,
|
||||
GAsyncResult *result,
|
||||
gpointer user_data)
|
||||
{
|
||||
GFile *file = G_FILE (object);
|
||||
MoveAsyncData *data = user_data;
|
||||
GError *error = NULL;
|
||||
|
||||
data->res = g_file_move_finish (file, result, &error);
|
||||
data->error = error;
|
||||
data->done = TRUE;
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
goffset total_num_bytes;
|
||||
} MoveAsyncProgressData;
|
||||
|
||||
static void
|
||||
test_move_async_progress_cb (goffset current_num_bytes,
|
||||
goffset total_num_bytes,
|
||||
gpointer user_data)
|
||||
{
|
||||
MoveAsyncProgressData *data = user_data;
|
||||
data->total_num_bytes = total_num_bytes;
|
||||
}
|
||||
|
||||
/* Test that move_async() moves the file correctly */
|
||||
static void
|
||||
test_move_async (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};
|
||||
|
||||
source = g_file_new_tmp ("g_file_move_XXXXXX", &iostream, NULL);
|
||||
|
||||
destination_path = g_build_path (G_DIR_SEPARATOR_S, g_get_tmp_dir (), "g_file_move_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 amount 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);
|
||||
|
||||
g_file_move_async (source,
|
||||
destination,
|
||||
G_FILE_COPY_NONE,
|
||||
0,
|
||||
NULL,
|
||||
test_move_async_progress_cb,
|
||||
&progress_data,
|
||||
test_move_async_cb,
|
||||
&data);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
@ -3049,6 +3154,7 @@ main (int argc, char *argv[])
|
||||
g_test_add_func ("/file/writev/async_all-to-big-vectors", test_writev_async_all_too_big_vectors);
|
||||
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);
|
||||
|
||||
return g_test_run ();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user