SpiceFileTransferTask has a callback to be called when operation ended. Til this patch, we were setting the user callback which means that in multiple file-transfers, we were calling the user callback several times. Following the same logic pointed from 113093dd00a1cf10f6d3c3589b7 this is a SpiceMainChannel operation and it should only call the user callback when this operation is over (FileTransferOperation now). --- src/channel-main.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 63 insertions(+), 6 deletions(-) diff --git a/src/channel-main.c b/src/channel-main.c index b75dfcf..2aaf0a8 100644 --- a/src/channel-main.c +++ b/src/channel-main.c @@ -154,6 +154,10 @@ typedef struct { SpiceMainChannel *channel; GFileProgressCallback progress_callback; gpointer progress_callback_data; + GAsyncReadyCallback end_callback; + gpointer end_callback_data; + GError *error; + GCancellable *cancellable; goffset total_sent; goffset transfer_size; } FileTransferOperation; @@ -1835,9 +1839,8 @@ static void file_xfer_close_cb(GObject *object, } } - /* Notify to user that files have been transferred or something error - happened. */ - task = g_task_new(self->priv->channel, + /* Notify channel-main if this file was successfully transferred or not */ + task = g_task_new(self, self->priv->cancellable, self->priv->callback, self->priv->user_data); @@ -1913,6 +1916,38 @@ static void file_xfer_flush_callback(SpiceFileTransferTask *xfer_task, file_xfer_flush_async(main_channel, cancellable, file_xfer_data_flushed_cb, xfer_task); } +static void file_xfer_end_callback(GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + FileTransferOperation *xfer_op = user_data; + GTask *task = G_TASK(res); + + if (!g_task_had_error(task) || xfer_op->error != NULL) + /* SpiceFileTransferTask and FileTransferOperation are freed on + * file_transfer_operation_task_finished */ + return; + + /* Get the GError from SpiceFileTransferTask so we can properly return to + * the application when the FileTransferOperation ends */ + g_task_propagate_boolean(task, &xfer_op->error); + + /* User can cancel a FileTransfer without cancelling the whole + * operation. For that, spice_main_file_copy_async must be called + * without GCancellabe */ + if (g_error_matches(xfer_op->error, G_IO_ERROR, G_IO_ERROR_CANCELLED) && + xfer_op->cancellable == NULL) { + SpiceFileTransferTask *xfer_task; + guint32 task_id; + + xfer_task = SPICE_FILE_TRANSFER_TASK(source_object); + g_object_get (xfer_task, "id", &task_id, NULL); + + spice_debug ("file-transfer %u was cancelled", task_id); + g_clear_error(&xfer_op->error); + } +} + /* main context */ static void file_xfer_read_cb(GObject *source_object, GAsyncResult *res, @@ -3103,10 +3138,24 @@ static void file_transfer_operation_end(FileTransferOperation *xfer_op) g_return_if_fail(xfer_op != NULL); spice_debug("Freeing file-transfer-operation %p", xfer_op); + if (xfer_op->end_callback) { + GTask *task = g_task_new(xfer_op->channel, + xfer_op->cancellable, + xfer_op->end_callback, + xfer_op->end_callback_data); + + if (xfer_op->error != NULL) { + g_task_return_error(task, xfer_op->error); + } else { + g_task_return_boolean(task, TRUE); + } + } + /* SpiceFileTransferTask itself is freed after it emits "finish" */ if (xfer_op->tasks != NULL) g_list_free(xfer_op->tasks); + g_clear_object (&xfer_op->cancellable); g_free(xfer_op); } @@ -3223,7 +3272,11 @@ static void task_finished(SpiceFileTransferTask *task, * files, please connect to the #SpiceMainChannel::new-file-transfer signal. * * When the operation is finished, callback will be called. You can then call - * spice_main_file_copy_finish() to get the result of the operation. + * spice_main_file_copy_finish() to get the result of the operation. Note that + * before release 0.32 the callback was called for each file in multiple file + * transfer. This behavior was changed for the same reason as the + * progress_callback (above). If you need to monitor the ending of individual + * files, you can connect to "finished" signal from each SpiceFileTransferTask. * **/ void spice_main_file_copy_async(SpiceMainChannel *channel, @@ -3257,15 +3310,19 @@ void spice_main_file_copy_async(SpiceMainChannel *channel, xfer_op = g_new0(FileTransferOperation, 1); xfer_op->progress_callback = progress_callback; xfer_op->progress_callback_data = progress_callback_data; + xfer_op->end_callback = callback; + xfer_op->end_callback_data = user_data; xfer_op->channel = channel; + xfer_op->error = NULL; + xfer_op->cancellable = (cancellable != NULL) ? g_object_ref(cancellable) : NULL; xfer_op->tasks = spice_file_transfer_task_create_tasks(channel, sources, flags, cancellable, file_xfer_flush_callback, xfer_op, - callback, - user_data); + file_xfer_end_callback, + xfer_op); spice_debug("New file-transfer-operation %p", xfer_op); for (it = xfer_op->tasks; it != NULL; it = it->next) { SpiceFileTransferTask *task = SPICE_FILE_TRANSFER_TASK(it->data); -- 2.5.5 _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/spice-devel