[Spice-devel] [PATCH v7 08/14] UsbDeviceManager: Implement asynchronous disconnect device flow

Jonathon Jongsma jjongsma at redhat.com
Tue Mar 15 19:36:35 UTC 2016


Rebased patch using GTask:
--------------------

>From 77c450ee865f8afb02bb03527fb507b22850f93a Mon Sep 17 00:00:00 2001
From: Kirill Moizik <kmoizik at redhat.com>
Date: Tue, 8 Mar 2016 16:05:55 +0200
Subject: [PATCH spice-gtk 08/14] UsbDeviceManager: Implement asynchronous
 disconnect device flow

This commit introduces functions for asynchronous disconnection flows.
Following commits will make use of those.

Thread safety is ensured the same way it was done for connection
flow in previous commits. Disconnect logic is protected by the same
locks that protect connect/usbredir/channel management logic.

Signed-off-by: Kirill Moizik <kmoizik at redhat.com>
Signed-off-by: Dmitry Fleytman <dfleytma at redhat.com>
---
 src/channel-usbredir-priv.h |  9 +++++
 src/channel-usbredir.c      | 34 ++++++++++++++++++
 src/map-file                |  2 ++
 src/usb-device-manager.c    | 84 +++++++++++++++++++++++++++++++++++++++++++++
 src/usb-device-manager.h    | 11 ++++++
 5 files changed, 140 insertions(+)

diff --git a/src/channel-usbredir-priv.h b/src/channel-usbredir-priv.h
index c987474..17e9716 100644
--- a/src/channel-usbredir-priv.h
+++ b/src/channel-usbredir-priv.h
@@ -33,6 +33,15 @@ G_BEGIN_DECLS
 void spice_usbredir_channel_set_context(SpiceUsbredirChannel *channel,
                                         libusb_context       *context);
 
+void spice_usbredir_channel_disconnect_device_async(SpiceUsbredirChannel
*channel,
+                                                    GCancellable *cancellable,
+                                                    GAsyncReadyCallback
callback,
+                                                    gpointer user_data);
+
+gboolean spice_usbredir_channel_disconnect_device_finish(SpiceUsbredirChannel
*channel,
+                                                         GAsyncResult        
 *res,
+                                                         GError             
 **err);
+
 /* Note the context must be set, and the channel must be brought up
    (through spice_channel_connect()), before calling this. */
 void spice_usbredir_channel_connect_device_async(
diff --git a/src/channel-usbredir.c b/src/channel-usbredir.c
index f492fc5..bab278c 100644
--- a/src/channel-usbredir.c
+++ b/src/channel-usbredir.c
@@ -425,6 +425,8 @@ void
spice_usbredir_channel_disconnect_device(SpiceUsbredirChannel *channel)
 
     CHANNEL_DEBUG(channel, "disconnecting device from usb channel %p",
channel);
 
+    spice_usbredir_channel_lock(channel);
+
     switch (priv->state) {
     case STATE_DISCONNECTED:
     case STATE_DISCONNECTING:
@@ -458,6 +460,38 @@ void
spice_usbredir_channel_disconnect_device(SpiceUsbredirChannel *channel)
         priv->state  = STATE_DISCONNECTED;
         break;
     }
+
+    spice_usbredir_channel_unlock(channel);
+}
+
+static void
+_disconnect_device_thread(GTask *task,
+                          gpointer object,
+                          gpointer task_data,
+                          GCancellable *cancellable)
+{
+    spice_usbredir_channel_disconnect_device(SPICE_USBREDIR_CHANNEL(object));
+    g_task_return_boolean(task, TRUE);
+}
+
+G_GNUC_INTERNAL
+gboolean spice_usbredir_channel_disconnect_device_finish(
+                                               SpiceUsbredirChannel *channel,
+                                               GAsyncResult         *res,
+                                               GError              **err)
+{
+    return g_task_propagate_boolean(G_TASK(res), err);
+}
+
+void spice_usbredir_channel_disconnect_device_async(SpiceUsbredirChannel
*channel,
+                                                    GCancellable *cancellable,
+                                                    GAsyncReadyCallback
callback,
+                                                    gpointer user_data)
+{
+    GTask* task = g_task_new(channel, cancellable, callback, user_data);
+
+    g_return_if_fail(channel != NULL);
+    g_task_run_in_thread(task, _disconnect_device_thread);
 }
 
 G_GNUC_INTERNAL
diff --git a/src/map-file b/src/map-file
index 123ef96..112b5ea 100644
--- a/src/map-file
+++ b/src/map-file
@@ -131,6 +131,8 @@ spice_usb_device_manager_can_redirect_device;
 spice_usb_device_manager_connect_device_async;
 spice_usb_device_manager_connect_device_finish;
 spice_usb_device_manager_disconnect_device;
+spice_usb_device_manager_disconnect_device_async;
+spice_usb_device_manager_disconnect_device_finish;
 spice_usb_device_manager_get;
 spice_usb_device_manager_get_devices;
 spice_usb_device_manager_get_devices_with_filter;
diff --git a/src/usb-device-manager.c b/src/usb-device-manager.c
index 8da5c0b..833db4b 100644
--- a/src/usb-device-manager.c
+++ b/src/usb-device-manager.c
@@ -1683,6 +1683,26 @@ gboolean spice_usb_device_manager_connect_device_finish(
     return g_task_propagate_boolean(task, err);
 }
 
+/**
+ * spice_usb_device_manager_disconnect_device_finish:
+ * @self: a #SpiceUsbDeviceManager.
+ * @res: a #GAsyncResult
+ * @err: (allow-none): a return location for a #GError, or %NULL.
+ *
+ * Finishes an async operation. See
spice_usb_device_manager_disconnect_device_async().
+ *
+ * Returns: %TRUE if disconnection is successful
+ */
+gboolean spice_usb_device_manager_disconnect_device_finish(
+    SpiceUsbDeviceManager *self, GAsyncResult *res, GError **err)
+{
+    GTask *task = G_TASK(res);
+
+    g_return_val_if_fail(g_task_is_valid(res, G_OBJECT(self)), FALSE);
+
+    return g_task_propagate_boolean(task, err);
+}
+
 #ifdef USE_USBREDIR
 static
 void _connect_device_async_cb(GObject *gobject,
@@ -1731,6 +1751,70 @@ void
spice_usb_device_manager_disconnect_device(SpiceUsbDeviceManager *self,
 #endif
 }
 
+typedef struct _disconnect_cb_data
+{
+    SpiceUsbDeviceManager  *self;
+    SpiceUsbDevice         *device;
+} disconnect_cb_data;
+
+#ifdef USE_USBREDIR
+static
+void _disconnect_device_async_cb(GObject *gobject,
+                                 GAsyncResult *channel_res,
+                                 gpointer user_data)
+{
+    SpiceUsbredirChannel  *channel = SPICE_USBREDIR_CHANNEL(gobject);
+    GTask                 *task    = user_data;
+    disconnect_cb_data    *data    = g_task_get_task_data(task);
+    GError *err = NULL;
+
+#ifdef G_OS_WIN32
+    SpiceUsbDeviceManager *self = SPICE_USB_DEVICE_MANAGER(data->self);
+
+    if (self->priv->use_usbclerk) {
+        _spice_usb_device_manager_uninstall_driver_async(self, data->device);
+    }
+#endif
+
+    spice_usbredir_channel_disconnect_device_finish(channel, channel_res,
&err);
+    if (err)
+        g_task_return_error(task, err);
+    else
+        g_task_return_boolean(task, TRUE);
+
+    g_object_unref(task);
+}
+#endif
+
+void spice_usb_device_manager_disconnect_device_async(SpiceUsbDeviceManager
*self,
+                                                      SpiceUsbDevice *device,
+                                                      GCancellable
*cancellable,
+                                                      GAsyncReadyCallback
callback,
+                                                      gpointer user_data)
+{
+#ifdef USE_USBREDIR
+    GTask *nested;
+    g_return_if_fail(SPICE_IS_USB_DEVICE_MANAGER(self));
+
+    g_return_if_fail(device != NULL);
+
+    SPICE_DEBUG("disconnecting device %p", device);
+
+    SpiceUsbredirChannel *channel;
+
+    channel = spice_usb_device_manager_get_channel_for_dev(self, device);
+    nested  = g_task_new(G_OBJECT(self), cancellable, callback, user_data);
+    disconnect_cb_data *data = g_new(disconnect_cb_data, 1);
+    data->self = self;
+    data->device = device;
+    g_task_set_task_data(nested, data, g_free);
+
+    spice_usbredir_channel_disconnect_device_async(channel, cancellable,
+                                                   _disconnect_device_async_cb,
+                                                   nested);
+#endif
+}
+
 /**
  * spice_usb_device_manager_can_redirect_device:
  * @self: the #SpiceUsbDeviceManager manager
diff --git a/src/usb-device-manager.h b/src/usb-device-manager.h
index e05ebae..d1b0a96 100644
--- a/src/usb-device-manager.h
+++ b/src/usb-device-manager.h
@@ -116,9 +116,20 @@ void spice_usb_device_manager_connect_device_async(
                                              GCancellable *cancellable,
                                              GAsyncReadyCallback callback,
                                              gpointer user_data);
+
+void spice_usb_device_manager_disconnect_device_async(
+                                             SpiceUsbDeviceManager *manager,
+                                             SpiceUsbDevice *device,
+                                             GCancellable *cancellable,
+                                             GAsyncReadyCallback callback,
+                                             gpointer user_data);
+
 gboolean spice_usb_device_manager_connect_device_finish(
     SpiceUsbDeviceManager *self, GAsyncResult *res, GError **err);
 
+gboolean spice_usb_device_manager_disconnect_device_finish(
+    SpiceUsbDeviceManager *self, GAsyncResult *res, GError **err);
+
 void spice_usb_device_manager_disconnect_device(SpiceUsbDeviceManager *manager,
                                                 SpiceUsbDevice *device);
 



On Tue, 2016-03-08 at 16:05 +0200, Dmitry Fleytman wrote:

> From: Kirill Moizik <kmoizik at redhat.com>
> 
> This commit introduces functions for asynchronous disconnection flows.
> Following commits will make use of those.
> 
> Thread safety is ensured the same way it was done for connection
> flow in previous commits. Disconnect logic is protected by the same
> locks that protect connect/usbredir/channel management logic.
> 
> Signed-off-by: Kirill Moizik <kmoizik at redhat.com>
> Signed-off-by: Dmitry Fleytman <dfleytma at redhat.com>
> ---
>  src/channel-usbredir-priv.h |  9 +++++
>  src/channel-usbredir.c      | 42 +++++++++++++++++++++
>  src/map-file                |  2 +
>  src/usb-device-manager.c    | 92
> +++++++++++++++++++++++++++++++++++++++++++++
>  src/usb-device-manager.h    | 11 ++++++
>  5 files changed, 156 insertions(+)
> 
> diff --git a/src/channel-usbredir-priv.h b/src/channel-usbredir-priv.h
> index c987474..17e9716 100644
> --- a/src/channel-usbredir-priv.h
> +++ b/src/channel-usbredir-priv.h
> @@ -33,6 +33,15 @@ G_BEGIN_DECLS
>  void spice_usbredir_channel_set_context(SpiceUsbredirChannel *channel,
>                                          libusb_context       *context);
>  
> +void spice_usbredir_channel_disconnect_device_async(SpiceUsbredirChannel
> *channel,
> +                                                    GCancellable
> *cancellable,
> +                                                    GAsyncReadyCallback
> callback,
> +                                                    gpointer user_data);
> +
> +gboolean spice_usbredir_channel_disconnect_device_finish(SpiceUsbredirChannel
> *channel,
> +                                                         GAsyncResult        
>  *res,
> +                                                         GError             
>  **err);
> +
>  /* Note the context must be set, and the channel must be brought up
>     (through spice_channel_connect()), before calling this. */
>  void spice_usbredir_channel_connect_device_async(
> diff --git a/src/channel-usbredir.c b/src/channel-usbredir.c
> index b33122a..7253ce6 100644
> --- a/src/channel-usbredir.c
> +++ b/src/channel-usbredir.c
> @@ -429,6 +429,8 @@ void
> spice_usbredir_channel_disconnect_device(SpiceUsbredirChannel *channel)
>  
>      CHANNEL_DEBUG(channel, "disconnecting device from usb channel %p",
> channel);
>  
> +    spice_usbredir_channel_lock(channel);
> +
>      switch (priv->state) {
>      case STATE_DISCONNECTED:
>      case STATE_DISCONNECTING:
> @@ -462,6 +464,46 @@ void
> spice_usbredir_channel_disconnect_device(SpiceUsbredirChannel *channel)
>          priv->state  = STATE_DISCONNECTED;
>          break;
>      }
> +
> +    spice_usbredir_channel_unlock(channel);
> +}
> +
> +static void
> +_disconnect_device_thread(GSimpleAsyncResult *simple,
> +                          GObject *object,
> +                          GCancellable *cancellable)
> +{
> +    spice_usbredir_channel_disconnect_device(SPICE_USBREDIR_CHANNEL(object));
> +}
> +
> +G_GNUC_INTERNAL
> +gboolean spice_usbredir_channel_disconnect_device_finish(
> +                                               SpiceUsbredirChannel *channel,
> +                                               GAsyncResult         *res,
> +                                               GError              **err)
> +{
> +    return g_simple_async_result_propagate_error(G_SIMPLE_ASYNC_RESULT(res),
> err);
> +}
> +
> +void spice_usbredir_channel_disconnect_device_async(SpiceUsbredirChannel
> *channel,
> +                                                    GCancellable
> *cancellable,
> +                                                    GAsyncReadyCallback
> callback,
> +                                                    gpointer user_data)
> +{
> +    GSimpleAsyncResult* result;
> +
> +    result = g_simple_async_result_new(G_OBJECT(channel),
> +                                       callback, user_data,
> +                                      
>  spice_usbredir_channel_disconnect_device_async);
> +
> +    if (channel) {
> +        g_simple_async_result_run_in_thread(result,
> +                                            _disconnect_device_thread,
> +                                            G_PRIORITY_DEFAULT,
> +                                            cancellable);
> +    } else {
> +        g_simple_async_result_complete_in_idle(result);
> +    }
>  }
>  
>  G_GNUC_INTERNAL
> diff --git a/src/map-file b/src/map-file
> index 92a9883..7f1dc2b 100644
> --- a/src/map-file
> +++ b/src/map-file
> @@ -127,6 +127,8 @@ spice_usb_device_manager_can_redirect_device;
>  spice_usb_device_manager_connect_device_async;
>  spice_usb_device_manager_connect_device_finish;
>  spice_usb_device_manager_disconnect_device;
> +spice_usb_device_manager_disconnect_device_async;
> +spice_usb_device_manager_disconnect_device_finish;
>  spice_usb_device_manager_get;
>  spice_usb_device_manager_get_devices;
>  spice_usb_device_manager_get_devices_with_filter;
> diff --git a/src/usb-device-manager.c b/src/usb-device-manager.c
> index fc0f15f..639e5cc 100644
> --- a/src/usb-device-manager.c
> +++ b/src/usb-device-manager.c
> @@ -1657,6 +1657,31 @@ gboolean
> spice_usb_device_manager_connect_device_finish(
>      return TRUE;
>  }
>  
> +/**
> + * spice_usb_device_manager_disconnect_device_finish:
> + * @self: a #SpiceUsbDeviceManager.
> + * @res: a #GAsyncResult
> + * @err: (allow-none): a return location for a #GError, or %NULL.
> + *
> + * Finishes an async operation. See
> spice_usb_device_manager_disconnect_device_async().
> + *
> + * Returns: %TRUE if disconnection is successful
> + */
> +gboolean spice_usb_device_manager_disconnect_device_finish(
> +    SpiceUsbDeviceManager *self, GAsyncResult *res, GError **err)
> +{
> +    GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT(res);
> +
> +    g_return_val_if_fail(g_simple_async_result_is_valid(res, G_OBJECT(self),
> +                              
>  spice_usb_device_manager_disconnect_device_async),
> +                         FALSE);
> +
> +    if (g_simple_async_result_propagate_error(simple, err))
> +        return FALSE;
> +
> +    return TRUE;
> +}
> +
>  #ifdef USE_USBREDIR
>  static
>  void _connect_device_async_cb(GObject *gobject,
> @@ -1705,6 +1730,73 @@ void
> spice_usb_device_manager_disconnect_device(SpiceUsbDeviceManager *self,
>  #endif
>  }
>  
> +typedef struct _disconnect_cb_data
> +{
> +    SpiceUsbDeviceManager  *self;
> +    GSimpleAsyncResult     *result;
> +    SpiceUsbDevice         *device;
> +} disconnect_cb_data;
> +
> +#ifdef USE_USBREDIR
> +static
> +void _disconnect_device_async_cb(GObject *gobject,
> +                                 GAsyncResult *channel_res,
> +                                 gpointer user_data)
> +{
> +    SpiceUsbredirChannel  *channel = SPICE_USBREDIR_CHANNEL(gobject);
> +    disconnect_cb_data    *data    = user_data;
> +    GSimpleAsyncResult    *result  = G_SIMPLE_ASYNC_RESULT(data->result);
> +    GError *err = NULL;
> +
> +#ifdef G_OS_WIN32
> +    SpiceUsbDeviceManager *self = SPICE_USB_DEVICE_MANAGER(data->self);
> +
> +    if (self->priv->use_usbclerk) {
> +        _spice_usb_device_manager_uninstall_driver_async(self, data->device);
> +    }
> +#endif
> +
> +    spice_usbredir_channel_disconnect_device_finish(channel, channel_res,
> &err);
> +    if (err) {
> +        g_simple_async_result_take_error(result, err);
> +    }
> +
> +    g_simple_async_result_complete(result);
> +    g_object_unref(result);
> +    g_free(data);
> +}
> +#endif
> +
> +void spice_usb_device_manager_disconnect_device_async(SpiceUsbDeviceManager
> *self,
> +                                                      SpiceUsbDevice *device,
> +                                                      GCancellable
> *cancellable,
> +                                                      GAsyncReadyCallback
> callback,
> +                                                      gpointer user_data)
> +{
> +#ifdef USE_USBREDIR
> +    GSimpleAsyncResult *nested;
> +    g_return_if_fail(SPICE_IS_USB_DEVICE_MANAGER(self));
> +
> +    g_return_if_fail(device != NULL);
> +
> +    SPICE_DEBUG("disconnecting device %p", device);
> +
> +    SpiceUsbredirChannel *channel;
> +
> +    channel = spice_usb_device_manager_get_channel_for_dev(self, device);
> +    nested  = g_simple_async_result_new(G_OBJECT(self), callback, user_data,
> +                             
>  spice_usb_device_manager_disconnect_device_async);
> +    disconnect_cb_data *data = g_new(disconnect_cb_data, 1);
> +    data->self = self;
> +    data->result = nested;
> +    data->device = device;
> +
> +    spice_usbredir_channel_disconnect_device_async(channel, cancellable,
> +                                                  
>  _disconnect_device_async_cb,
> +                                                   data);
> +#endif
> +}
> +
>  /**
>   * spice_usb_device_manager_can_redirect_device:
>   * @self: the #SpiceUsbDeviceManager manager
> diff --git a/src/usb-device-manager.h b/src/usb-device-manager.h
> index e05ebae..d1b0a96 100644
> --- a/src/usb-device-manager.h
> +++ b/src/usb-device-manager.h
> @@ -116,9 +116,20 @@ void spice_usb_device_manager_connect_device_async(
>                                               GCancellable *cancellable,
>                                               GAsyncReadyCallback callback,
>                                               gpointer user_data);
> +
> +void spice_usb_device_manager_disconnect_device_async(
> +                                             SpiceUsbDeviceManager *manager,
> +                                             SpiceUsbDevice *device,
> +                                             GCancellable *cancellable,
> +                                             GAsyncReadyCallback callback,
> +                                             gpointer user_data);
> +
>  gboolean spice_usb_device_manager_connect_device_finish(
>      SpiceUsbDeviceManager *self, GAsyncResult *res, GError **err);
>  
> +gboolean spice_usb_device_manager_disconnect_device_finish(
> +    SpiceUsbDeviceManager *self, GAsyncResult *res, GError **err);
> +
>  void spice_usb_device_manager_disconnect_device(SpiceUsbDeviceManager
> *manager,
>                                                  SpiceUsbDevice *device);
>  


More information about the Spice-devel mailing list