[Spice-devel] [spice-gtk v2 07/16] file-xfer: call user callback once per operation
Victor Toso
victortoso at redhat.com
Mon May 23 11:50:44 UTC 2016
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
More information about the Spice-devel
mailing list