[Spice-devel] [spice-gtk 5/5] usb-redir: move USB events handling to USB backend

Yuri Benditovich yuri.benditovich at daynix.com
Mon Jul 22 13:34:48 UTC 2019


Ping

On Sun, Jul 14, 2019 at 5:07 PM Yuri Benditovich
<yuri.benditovich at daynix.com> wrote:
>
> Before this commit:
> usb-device-manager starts thread for handling libusb events.
>   On Linux - from the beginning (as it is needed for hotplug
>   callbacks)
>   On Windows - starts it on first redirection and stops when
>   there are no redirections (it can't keep the thread when
>   there are no redirections as with libusb < 1.0.21 it will
>   not be able to force the thread to exit as there are no events)
> Current commit moves the event thread and handling events
>   completely to usb backend; usb-device-manager and other
>   are not aware of libusb and should not assume what it
>   needs to work. We start the event thread from the beginning
>   on both Linux and Windows and on Linux it works only for
>   hotplug callbacks, on Windows - just waits until device
>   redirection starts. On dispose of usb-device-manager
>   (when hotplug callbacks are deregistered), we interrupt
>   the thread once to stop it.
> This removes many lines of code and also removes all the
> differences between Linux and Windows in usb-device-manager.
>
> Signed-off-by: Yuri Benditovich <yuri.benditovich at daynix.com>
> ---
>  src/channel-usbredir.c        | 28 -------------
>  src/usb-backend.c             | 48 ++++++++++++++-------
>  src/usb-backend.h             |  2 -
>  src/usb-device-manager-priv.h |  6 ---
>  src/usb-device-manager.c      | 79 -----------------------------------
>  5 files changed, 33 insertions(+), 130 deletions(-)
>
> diff --git a/src/channel-usbredir.c b/src/channel-usbredir.c
> index 04acf0b..8d4cd66 100644
> --- a/src/channel-usbredir.c
> +++ b/src/channel-usbredir.c
> @@ -72,7 +72,6 @@ struct _SpiceUsbredirChannelPrivate {
>      SpiceUsbAclHelper *acl_helper;
>  #endif
>      GMutex device_connect_mutex;
> -    SpiceUsbDeviceManager *usb_device_manager;
>  };
>
>  static void channel_set_handlers(SpiceChannelClass *klass);
> @@ -169,11 +168,6 @@ static void spice_usbredir_channel_dispose(GObject *obj)
>      SpiceUsbredirChannel *channel = SPICE_USBREDIR_CHANNEL(obj);
>
>      spice_usbredir_channel_disconnect_device(channel);
> -    /* This should have been set to NULL during device disconnection,
> -     * but better not to leak it if this does not happen for some reason
> -     */
> -    g_warn_if_fail(channel->priv->usb_device_manager == NULL);
> -    g_clear_object(&channel->priv->usb_device_manager);
>
>      /* Chain up to the parent class */
>      if (G_OBJECT_CLASS(spice_usbredir_channel_parent_class)->dispose)
> @@ -248,8 +242,6 @@ static gboolean spice_usbredir_channel_open_device(
>      SpiceUsbredirChannel *channel, GError **err)
>  {
>      SpiceUsbredirChannelPrivate *priv = channel->priv;
> -    SpiceSession *session;
> -    SpiceUsbDeviceManager *manager;
>
>      g_return_val_if_fail(priv->state == STATE_DISCONNECTED
>  #ifdef USE_POLKIT
> @@ -265,16 +257,6 @@ static gboolean spice_usbredir_channel_open_device(
>          return FALSE;
>      }
>
> -    session = spice_channel_get_session(SPICE_CHANNEL(channel));
> -    manager = spice_usb_device_manager_get(session, NULL);
> -    g_return_val_if_fail(manager != NULL, FALSE);
> -
> -    priv->usb_device_manager = g_object_ref(manager);
> -    if (!spice_usb_device_manager_start_event_listening(priv->usb_device_manager, err)) {
> -        spice_usb_backend_channel_detach(priv->host);
> -        return FALSE;
> -    }
> -
>      priv->state = STATE_CONNECTED;
>
>      return TRUE;
> @@ -445,16 +427,6 @@ void spice_usbredir_channel_disconnect_device(SpiceUsbredirChannel *channel)
>          break;
>  #endif
>      case STATE_CONNECTED:
> -        /*
> -         * This sets the usb event thread run condition to FALSE, therefor
> -         * it must be done before usbredirhost_set_device NULL, as
> -         * usbredirhost_set_device NULL will interrupt the
> -         * libusb_handle_events call in the thread.
> -         */
> -        g_warn_if_fail(priv->usb_device_manager != NULL);
> -        spice_usb_device_manager_stop_event_listening(priv->usb_device_manager);
> -        g_clear_object(&priv->usb_device_manager);
> -
>          /* This also closes the libusb handle we passed from open_device */
>          spice_usb_backend_channel_detach(priv->host);
>          g_clear_pointer(&priv->device, spice_usb_backend_device_unref);
> diff --git a/src/usb-backend.c b/src/usb-backend.c
> index 829d81d..37db951 100644
> --- a/src/usb-backend.c
> +++ b/src/usb-backend.c
> @@ -58,6 +58,9 @@ struct _SpiceUsbBackend
>      usb_hot_plug_callback hotplug_callback;
>      void *hotplug_user_data;
>      libusb_hotplug_callback_handle hotplug_handle;
> +    GThread *event_thread;
> +    gint event_thread_run;
> +
>  #ifdef G_OS_WIN32
>      HANDLE hWnd;
>      libusb_device **libusb_device_list;
> @@ -406,28 +409,25 @@ SpiceUsbBackend *spice_usb_backend_new(GError **error)
>      return be;
>  }
>
> -gboolean spice_usb_backend_handle_events(SpiceUsbBackend *be)
> +static gpointer handle_libusb_events(gpointer user_data)
>  {
> +    SpiceUsbBackend *be = (SpiceUsbBackend *)user_data;
>      SPICE_DEBUG("%s >>", __FUNCTION__);
> -    gboolean ok = FALSE;
> -    if (be->libusb_context) {
> -        int res = libusb_handle_events(be->libusb_context);
> -        ok = res == 0;
> +    int res = 0;
> +    const char *desc = "";
> +    while (g_atomic_int_get(&be->event_thread_run)) {
> +        res = libusb_handle_events(be->libusb_context);
>          if (res && res != LIBUSB_ERROR_INTERRUPTED) {
> -            const char *desc = libusb_strerror(res);
> +            desc = libusb_strerror(res);
>              g_warning("Error handling USB events: %s [%i]", desc, res);
> -            ok = FALSE;
> +            break;
>          }
>      }
> -    SPICE_DEBUG("%s << %d", __FUNCTION__, ok);
> -    return ok;
> -}
> -
> -void spice_usb_backend_interrupt_event_handler(SpiceUsbBackend *be)
> -{
> -    if (be->libusb_context) {
> -        libusb_interrupt_event_handler(be->libusb_context);
> +    if (be->event_thread_run) {
> +        SPICE_DEBUG("%s: the thread aborted, %s(%d)", __FUNCTION__, desc, res);
>      }
> +    SPICE_DEBUG("%s <<", __FUNCTION__);
> +    return NULL;
>  }
>
>  void spice_usb_backend_deregister_hotplug(SpiceUsbBackend *be)
> @@ -438,6 +438,12 @@ void spice_usb_backend_deregister_hotplug(SpiceUsbBackend *be)
>          be->hotplug_handle = 0;
>      }
>      be->hotplug_callback = NULL;
> +    g_atomic_int_set(&be->event_thread_run, FALSE);
> +    if (be->event_thread) {
> +        libusb_interrupt_event_handler(be->libusb_context);
> +        g_thread_join(be->event_thread);
> +        be->event_thread = NULL;
> +    }
>  }
>
>  gboolean spice_usb_backend_register_hotplug(SpiceUsbBackend *be,
> @@ -461,6 +467,16 @@ gboolean spice_usb_backend_register_hotplug(SpiceUsbBackend *be,
>             "Error on USB hotplug detection: %s [%i]", desc, rc);
>          return FALSE;
>      }
> +
> +    g_atomic_int_set(&be->event_thread_run, TRUE);
> +    be->event_thread = g_thread_try_new("usb_ev_thread",
> +                                    handle_libusb_events,
> +                                    be, error);
> +    if (!be->event_thread) {
> +        g_warning("Error starting event thread");
> +        spice_usb_backend_deregister_hotplug(be);
> +        return FALSE;
> +    }
>      return TRUE;
>  }
>
> @@ -468,6 +484,8 @@ void spice_usb_backend_delete(SpiceUsbBackend *be)
>  {
>      g_return_if_fail(be != NULL);
>      SPICE_DEBUG("%s >>", __FUNCTION__);
> +    /* just to be on the safe side if not deregistered */
> +    spice_usb_backend_deregister_hotplug(be);
>      if (be->libusb_context) {
>          libusb_exit(be->libusb_context);
>      }
> diff --git a/src/usb-backend.h b/src/usb-backend.h
> index 814da46..69a490b 100644
> --- a/src/usb-backend.h
> +++ b/src/usb-backend.h
> @@ -56,8 +56,6 @@ enum {
>  SpiceUsbBackend *spice_usb_backend_new(GError **error);
>  void spice_usb_backend_delete(SpiceUsbBackend *context);
>
> -gboolean spice_usb_backend_handle_events(SpiceUsbBackend *be);
> -void spice_usb_backend_interrupt_event_handler(SpiceUsbBackend *be);
>  gboolean spice_usb_backend_register_hotplug(SpiceUsbBackend *be,
>                                              void *user_data,
>                                              usb_hot_plug_callback proc,
> diff --git a/src/usb-device-manager-priv.h b/src/usb-device-manager-priv.h
> index 39aaf2f..66acf6d 100644
> --- a/src/usb-device-manager-priv.h
> +++ b/src/usb-device-manager-priv.h
> @@ -25,12 +25,6 @@
>
>  G_BEGIN_DECLS
>
> -gboolean spice_usb_device_manager_start_event_listening(
> -    SpiceUsbDeviceManager *manager, GError **err);
> -
> -void spice_usb_device_manager_stop_event_listening(
> -    SpiceUsbDeviceManager *manager);
> -
>  #ifdef USE_USBREDIR
>  void spice_usb_device_manager_device_error(
>      SpiceUsbDeviceManager *manager, SpiceUsbDevice *device, GError *err);
> diff --git a/src/usb-device-manager.c b/src/usb-device-manager.c
> index 7105ff1..25fac04 100644
> --- a/src/usb-device-manager.c
> +++ b/src/usb-device-manager.c
> @@ -93,9 +93,6 @@ struct _SpiceUsbDeviceManagerPrivate {
>      gchar *redirect_on_connect;
>  #ifdef USE_USBREDIR
>      SpiceUsbBackend *context;
> -    int event_listeners;
> -    GThread *event_thread;
> -    gint event_thread_run;
>      struct usbredirfilter_rule *auto_conn_filter_rules;
>      struct usbredirfilter_rule *redirect_on_connect_rules;
>      int auto_conn_filter_rules_count;
> @@ -262,9 +259,6 @@ static gboolean spice_usb_device_manager_initable_init(GInitable  *initable,
>          spice_usb_backend_delete(priv->context);
>          return FALSE;
>      }
> -#ifndef G_OS_WIN32
> -    spice_usb_device_manager_start_event_listening(self, NULL);
> -#endif
>
>      /* Start listening for usb channels connect/disconnect */
>      spice_g_signal_connect_object(priv->session, "channel-new", G_CALLBACK(channel_new), self, G_CONNECT_AFTER);
> @@ -286,27 +280,8 @@ static void spice_usb_device_manager_dispose(GObject *gobject)
>      SpiceUsbDeviceManager *self = SPICE_USB_DEVICE_MANAGER(gobject);
>      SpiceUsbDeviceManagerPrivate *priv = self->priv;
>
> -#ifndef G_OS_WIN32
> -    spice_usb_device_manager_stop_event_listening(self);
> -    if (g_atomic_int_get(&priv->event_thread_run)) {
> -        /* Force termination of the event thread even if there were some
> -         * mismatched spice_usb_device_manager_{start,stop}_event_listening
> -         * calls. Otherwise, the usb event thread will be leaked, and will
> -         * try to use the libusb context we destroy in finalize(), which would
> -         * cause a crash */
> -        g_warn_if_reached();
> -        g_atomic_int_set(&priv->event_thread_run, FALSE);
> -    }
> -#endif
>      spice_usb_backend_deregister_hotplug(priv->context);
>
> -    if (priv->event_thread) {
> -        g_warn_if_fail(g_atomic_int_get(&priv->event_thread_run) == FALSE);
> -        g_atomic_int_set(&priv->event_thread_run, FALSE);
> -        spice_usb_backend_interrupt_event_handler(priv->context);
> -        g_thread_join(priv->event_thread);
> -        priv->event_thread = NULL;
> -    }
>  #endif
>
>      /* Chain up to the parent class */
> @@ -324,7 +299,6 @@ static void spice_usb_device_manager_finalize(GObject *gobject)
>      if (priv->devices) {
>          g_ptr_array_unref(priv->devices);
>      }
> -    g_return_if_fail(priv->event_thread == NULL);
>      if (priv->context) {
>          spice_usb_backend_delete(priv->context);
>      }
> @@ -916,59 +890,6 @@ static void spice_usb_device_manager_channel_connect_cb(
>  /* ------------------------------------------------------------------ */
>  /* private api                                                        */
>
> -static gpointer spice_usb_device_manager_usb_ev_thread(gpointer user_data)
> -{
> -    SpiceUsbDeviceManager *self = SPICE_USB_DEVICE_MANAGER(user_data);
> -    SpiceUsbDeviceManagerPrivate *priv = self->priv;
> -
> -    while (g_atomic_int_get(&priv->event_thread_run)) {
> -        if (!spice_usb_backend_handle_events(priv->context)) {
> -            break;
> -        }
> -    }
> -
> -    return NULL;
> -}
> -
> -gboolean spice_usb_device_manager_start_event_listening(
> -    SpiceUsbDeviceManager *self, GError **err)
> -{
> -    SpiceUsbDeviceManagerPrivate *priv = self->priv;
> -
> -    g_return_val_if_fail(err == NULL || *err == NULL, FALSE);
> -
> -    priv->event_listeners++;
> -    if (priv->event_listeners > 1)
> -        return TRUE;
> -
> -    /* We don't join the thread when we stop event listening, as the
> -       libusb_handle_events call in the thread won't exit until the
> -       libusb_close call for the device is made from usbredirhost_close. */
> -    if (priv->event_thread) {
> -        g_atomic_int_set(&priv->event_thread_run, FALSE);
> -        spice_usb_backend_interrupt_event_handler(priv->context);
> -         g_thread_join(priv->event_thread);
> -         priv->event_thread = NULL;
> -    }
> -    g_atomic_int_set(&priv->event_thread_run, TRUE);
> -    priv->event_thread = g_thread_new("usb_ev_thread",
> -                                      spice_usb_device_manager_usb_ev_thread,
> -                                      self);
> -    return priv->event_thread != NULL;
> -}
> -
> -void spice_usb_device_manager_stop_event_listening(
> -    SpiceUsbDeviceManager *self)
> -{
> -    SpiceUsbDeviceManagerPrivate *priv = self->priv;
> -
> -    g_return_if_fail(priv->event_listeners > 0);
> -
> -    priv->event_listeners--;
> -    if (priv->event_listeners == 0)
> -        g_atomic_int_set(&priv->event_thread_run, FALSE);
> -}
> -
>  static void spice_usb_device_manager_check_redir_on_connect(
>      SpiceUsbDeviceManager *self, SpiceChannel *channel)
>  {
> --
> 2.17.1
>


More information about the Spice-devel mailing list