[Spice-devel,spice-gtk,v2,07/16] file-xfer: call user callback once per operation

Submitted by Victor Toso on May 23, 2016, 11:50 a.m.

Details

Message ID 1464004253-17924-8-git-send-email-victortoso@redhat.com
State Superseded
Headers show
Series "separate SpiceFileTransferTask logic from channel-main" ( rev: 1 ) in Spice

Not browsing as part of any series.

Commit Message

Victor Toso May 23, 2016, 11:50 a.m.
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(-)

Patch hide | download patch | download mbox

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);