[Spice-devel] [spice-gtk v4 14/24] file-xfer: call progress_callback per FileTransferOperation

Jonathon Jongsma jjongsma at redhat.com
Mon Jun 27 16:27:38 UTC 2016


On Thu, 2016-06-23 at 19:37 +0200, Victor Toso wrote:
> Before this patch, the progress_callback is being called from
> SpiceFileTransferTask each time that some data is flushed to the agent
> of this given xfer-task. The progress value is computed by iterating
> on all available xfer-tasks.
> 
> This patch intend to fix/change:
> 
> * The progress_callback should be called only with information related
>   to the FileTransferOperation of given xfer-task; This was also
>   suggested by 113093dd00a1cf10f6d3c3589b7589a184cec081;
> 
> * The progress_callback should not be done from SpiceFileTransferTask
>   context by (proposed) design. As the transfer operation starts in
>   spice_main_file_copy_async(), FileTransferOperation should handle
>   these calls.
> ---
>  src/channel-main.c | 102 ++++++++++++++++++++++++++++++--------------------
> ---
>  1 file changed, 57 insertions(+), 45 deletions(-)
> 
> diff --git a/src/channel-main.c b/src/channel-main.c
> index 117c735..fba5b7f 100644
> --- a/src/channel-main.c
> +++ b/src/channel-main.c
> @@ -61,8 +61,6 @@ static GHashTable
> *spice_file_transfer_task_create_tasks(GFile **files,
>                                                           SpiceMainChannel
> *channel,
>                                                           GFileCopyFlags
> flags,
>                                                           GCancellable
> *cancellable,
> -                                                         GFileProgressCallbac
> k progress_callback,
> -                                                         gpointer
> progress_callback_data,
>                                                           GAsyncReadyCallback
> callback,
>                                                           gpointer user_data);
>  static void spice_file_transfer_task_init_task_async(SpiceFileTransferTask
> *self,
> @@ -78,6 +76,8 @@ static gssize
> spice_file_transfer_task_read_finish(SpiceFileTransferTask *self,
>                                                     GAsyncResult *result,
>                                                     char **buffer,
>                                                     GError **error);
> +static guint64 spice_file_transfer_task_get_file_size(SpiceFileTransferTask
> *self);
> +static guint64 spice_file_transfer_task_get_bytes_read(SpiceFileTransferTask
> *self);
>  
>  /**
>   * SECTION:file-transfer-task
> @@ -108,8 +108,6 @@ struct _SpiceFileTransferTask
>      GFileInputStream               *file_stream;
>      GFileCopyFlags                 flags;
>      GCancellable                   *cancellable;
> -    GFileProgressCallback          progress_callback;
> -    gpointer                       progress_callback_data;
>      GAsyncReadyCallback            callback;
>      gpointer                       user_data;
>      char                           *buffer;
> @@ -162,6 +160,12 @@ typedef struct {
>  typedef struct {
>      GHashTable                 *xfer_task_ht;
>      SpiceMainChannel           *channel;
> +    GFileProgressCallback       progress_callback;
> +    gpointer                    progress_callback_data;
> +    struct {
> +        goffset                 total_sent;
> +        goffset                 transfer_size;
> +    } stats;
>  } FileTransferOperation;
>  
>  struct _SpiceMainChannelPrivate  {
> @@ -274,6 +278,7 @@ static SpiceFileTransferTask
> *file_transfer_operation_find_task_by_id(SpiceMainC
>  static void file_transfer_operation_task_finished(SpiceFileTransferTask
> *xfer_task,
>                                                    GError *error,
>                                                    gpointer userdata);
> +static void file_transfer_operation_send_progress(SpiceFileTransferTask
> *xfer_task);
>  
>  /* ------------------------------------------------------------------ */
>  
> @@ -1884,39 +1889,6 @@ static void file_xfer_close_cb(GObject      *object,
>      g_object_unref(self);
>  }
>  
> -static void file_xfer_send_progress(SpiceFileTransferTask *xfer_task)
> -{
> -    goffset read = 0;
> -    goffset total = 0;
> -    GHashTableIter iter;
> -    gpointer key, value;
> -    SpiceMainChannel *channel;
> -
> -    g_return_if_fail(xfer_task != NULL);
> -
> -    if (!xfer_task->progress_callback)
> -        return;
> -
> -    channel = spice_file_transfer_task_get_channel(xfer_task);
> -
> -    /* FIXME: This iterates to all SpiceFileTransferTasks, including ones
> from
> -     * different FileTransferOperation. The progress_callback should only
> return
> -     * the info related to its FileTransferOperation */
> -    /* since the progress_callback does not have a parameter to indicate
> -     * which file the progress is associated with, report progress on all
> -     * current transfers */
> -    g_hash_table_iter_init(&iter, channel->priv->file_xfer_tasks);
> -    while (g_hash_table_iter_next(&iter, &key, &value)) {
> -        SpiceFileTransferTask *t;
> -
> -        t = file_transfer_operation_find_task_by_id(channel,
> GPOINTER_TO_UINT(key));
> -        read += t->read_bytes;
> -        total += t->file_size;
> -    }
> -
> -    xfer_task->progress_callback(read, total, xfer_task-
> >progress_callback_data);
> -}
> -
>  static void file_xfer_data_flushed_cb(GObject *source_object,
>                                        GAsyncResult *res,
>                                        gpointer user_data)
> @@ -1931,7 +1903,7 @@ static void file_xfer_data_flushed_cb(GObject
> *source_object,
>          return;
>      }
>  
> -    file_xfer_send_progress(self);
> +    file_transfer_operation_send_progress(self);
>  
>      if (spice_util_get_debug()) {
>          const GTimeSpan interval = 20 * G_TIME_SPAN_SECOND;
> @@ -1971,11 +1943,13 @@ static void file_xfer_read_async_cb(GObject
> *source_object,
>                                      GAsyncResult *res,
>                                      gpointer user_data)
>  {
> +    FileTransferOperation *xfer_op;
>      SpiceFileTransferTask *xfer_task;
>      SpiceMainChannel *channel;
>      gssize count;
>      char *buffer;
>      GCancellable *cancellable;
> +    guint32 task_id;
>      GError *error = NULL;
>  
>      xfer_task = SPICE_FILE_TRANSFER_TASK(source_object);
> @@ -1993,6 +1967,10 @@ static void file_xfer_read_async_cb(GObject
> *source_object,
>          /* on EOF just wait for VD_AGENT_FILE_XFER_STATUS from agent */
>          return;
>  
> +    task_id = spice_file_transfer_task_get_id(xfer_task);
> +    xfer_op = g_hash_table_lookup(channel->priv->file_xfer_tasks,
> GUINT_TO_POINTER(task_id));
> +    xfer_op->stats.total_sent += count;
> +
>      cancellable = spice_file_transfer_task_get_cancellable(xfer_task);
>      file_xfer_flush_async(channel, cancellable, file_xfer_data_flushed_cb,
> xfer_task);
>  }
> @@ -3019,6 +2997,7 @@ static void file_xfer_init_task_async_cb(GObject *obj,
> GAsyncResult *res, gpoint
>      VDAgentFileXferStartMessage msg;
>      guint64 file_size;
>      gsize data_len;
> +    FileTransferOperation *xfer_op;
>      GError *error = NULL;
>  
>      xfer_task = SPICE_FILE_TRANSFER_TASK(obj);
> @@ -3031,6 +3010,9 @@ static void file_xfer_init_task_async_cb(GObject *obj,
> GAsyncResult *res, gpoint
>      basename = g_file_info_get_attribute_as_string(info,
> G_FILE_ATTRIBUTE_STANDARD_NAME);
>      file_size = g_file_info_get_attribute_uint64(info,
> G_FILE_ATTRIBUTE_STANDARD_SIZE);
>  
> +    xfer_op = data;
> +    xfer_op->stats.transfer_size += file_size;
> +
>      keyfile = g_key_file_new();
>      g_key_file_set_string(keyfile, "vdagent-file-xfer", "name", basename);
>      g_key_file_set_uint64(keyfile, "vdagent-file-xfer", "size", file_size);
> @@ -3129,6 +3111,11 @@ static void
> file_transfer_operation_task_finished(SpiceFileTransferTask *xfer_ta
>          return;
>      }
>  
> +    if (error) {
> +        xfer_op->stats.total_sent -=
> spice_file_transfer_task_get_bytes_read(xfer_task);
> +        xfer_op->stats.transfer_size -=
> spice_file_transfer_task_get_file_size(xfer_task);
> +    }
> +

So, this can cause the "bytes read" value to decrease from one update to the
next. I don't think that will cause any problems with the current client code,
but it could be a little bit surprising to the API user. One alternative may be
to simply consider the task to be completely read when it hits an error. In
other words, something like:

if (error) {
	xfer_op->stats.transfer_size -= (file_size - bytes_read)
}

That way, the total size left to transfer drops slightly, but the number of
bytes read remains monotonically increasing. I don't know if this is necessarily
better than your approach, but I just thought I'd throw it out for discussion.
opinion?



>      /* Remove and free SpiceFileTransferTask */
>      g_hash_table_remove(xfer_op->xfer_task_ht, GUINT_TO_POINTER(task_id));
>      g_object_unref(xfer_task);
> @@ -3141,6 +3128,23 @@ static void
> file_transfer_operation_task_finished(SpiceFileTransferTask *xfer_ta
>          file_transfer_operation_end(xfer_op);
>  }
>  
> +static void file_transfer_operation_send_progress(SpiceFileTransferTask
> *xfer_task)
> +{
> +    FileTransferOperation *xfer_op;
> +    SpiceMainChannel *channel;
> +    guint32 task_id;
> +
> +    channel = spice_file_transfer_task_get_channel(xfer_task);
> +    task_id = spice_file_transfer_task_get_id(xfer_task);
> +    xfer_op = g_hash_table_lookup(channel->priv->file_xfer_tasks,
> GUINT_TO_POINTER(task_id));
> +    g_return_if_fail(xfer_op != NULL);
> +
> +    if (xfer_op->progress_callback)
> +        xfer_op->progress_callback(xfer_op->stats.total_sent,
> +                                   xfer_op->stats.transfer_size,
> +                                   xfer_op->progress_callback_data);
> +}
> +

This strikes me as a little bit convoluted. The function is ostensibly a
FileTransferOperation method (by the name and by the behavior), yet the function
argument is SpiceFileTransferTask*. And to get from SpiceFileTransferTask to
FileTransferOperation, we have to first get the SpiceMainChannel and ID from the
task, then look up the operation from a hash table in the channel. This is one
of the reasons that I thought it might be better for the task structure to have
a reference to its operation rather than storing the operation in a hash table
in the channel indexed on task ID. But perhaps my solution won't work out very
well in real life, and I may just being overly nitpick-y here. Feel free to tell
me so if I am ;)

>  static SpiceFileTransferTask *spice_file_transfer_task_new(SpiceMainChannel
> *channel,
>                                                             GFile *file,
>                                                             GCancellable
> *cancellable);
> @@ -3210,12 +3214,12 @@ void spice_main_file_copy_async(SpiceMainChannel
> *channel,
>  
>      xfer_op = g_new0(FileTransferOperation, 1);
>      xfer_op->channel = channel;
> +    xfer_op->progress_callback = progress_callback;
> +    xfer_op->progress_callback_data = progress_callback_data;
>      xfer_op->xfer_task_ht = spice_file_transfer_task_create_tasks(sources,
>                                                                    channel,
>                                                                    flags,
>                                                                    cancellable
> ,
> -                                                                  progress_ca
> llback,
> -                                                                  progress_ca
> llback_data,
>                                                                    callback,
>                                                                    user_data);
>      g_hash_table_iter_init(&iter, xfer_op->xfer_task_ht);
> @@ -3234,7 +3238,7 @@ void spice_main_file_copy_async(SpiceMainChannel
> *channel,
>  
>          spice_file_transfer_task_init_task_async(xfer_task,
>                                                   file_xfer_init_task_async_cb
> ,
> -                                                 NULL);
> +                                                 xfer_op);
>      }
>  }
>  
> @@ -3279,6 +3283,18 @@ static GCancellable
> *spice_file_transfer_task_get_cancellable(SpiceFileTransferT
>      return self->cancellable;
>  }
>  
> +static guint64 spice_file_transfer_task_get_file_size(SpiceFileTransferTask
> *self)
> +{
> +    g_return_val_if_fail(self != NULL, 0);
> +    return self->file_size;
> +}
> +
> +static guint64 spice_file_transfer_task_get_bytes_read(SpiceFileTransferTask
> *self)
> +{
> +    g_return_val_if_fail(self != NULL, 0);
> +    return self->read_bytes;
> +}
> +
>  /* Helper function which only creates a SpiceFileTransferTask per GFile
>   * in @files and returns a HashTable mapping task-id to the task itself
>   * Note that the HashTable does not free its values uppon destruction:
> @@ -3288,8 +3304,6 @@ static GHashTable
> *spice_file_transfer_task_create_tasks(GFile **files,
>                                                           SpiceMainChannel
> *channel,
>                                                           GFileCopyFlags
> flags,
>                                                           GCancellable
> *cancellable,
> -                                                         GFileProgressCallbac
> k progress_callback,
> -                                                         gpointer
> progress_callback_data,
>                                                           GAsyncReadyCallback
> callback,
>                                                           gpointer user_data)
>  {
> @@ -3312,8 +3326,6 @@ static GHashTable
> *spice_file_transfer_task_create_tasks(GFile **files,
>  
>          xfer_task = spice_file_transfer_task_new(channel, files[i],
> task_cancellable);
>          xfer_task->flags = flags;
> -        xfer_task->progress_callback = progress_callback;
> -        xfer_task->progress_callback_data = progress_callback_data;
>          xfer_task->callback = callback;
>          xfer_task->user_data = user_data;
>  


looks good other than discussion points above

Reviewed-by: Jonathon Jongsma <jjongsma at redhat.com>




More information about the Spice-devel mailing list