diff options
author | Benjamin Berg <bberg@redhat.com> | 2020-09-30 23:34:07 +0200 |
---|---|---|
committer | Benjamin Berg <bberg@redhat.com> | 2020-10-01 00:02:08 +0200 |
commit | e3ce22315687529768dc1a3b407f4bcca956bd09 (patch) | |
tree | 643791dfa27e98cd04342e468d1d7d0e0e2441b9 | |
parent | 9ce9a6c99be107bc11a5fa2bd28a58e54fc27958 (diff) | |
download | gusb-e3ce22315687529768dc1a3b407f4bcca956bd09.tar.gz |
device: Fix cancellation if cancellable is already cancelled
libusb can only handle cancellation if the transfer is already
in-flight. However, g_cancellable_connect will immediately call the
callback and then libusb_cancel_transfer when the cancellable is already
cancelled at the time the transfer is submitted.
Move the cancellation registration to after transfer submission to avoid
ordering issues. Also avoid even submitting the transfer if the
cancellable is already cancelled to begin with.
-rw-r--r-- | gusb/gusb-device.c | 69 |
1 files changed, 42 insertions, 27 deletions
diff --git a/gusb/gusb-device.c b/gusb/gusb-device.c index 25f1bcf..248b6f3 100644 --- a/gusb/gusb-device.c +++ b/gusb/gusb-device.c @@ -1267,18 +1267,14 @@ g_usb_device_control_transfer_async (GUsbDevice *device, req->transfer = libusb_alloc_transfer (0); req->data = data; - /* setup cancellation */ - if (cancellable != NULL) { - req->cancellable = g_object_ref (cancellable); - req->cancellable_id = g_cancellable_connect (req->cancellable, - G_CALLBACK (g_usb_device_cancelled_cb), - req, - NULL); - } - task = g_task_new (device, cancellable, callback, user_data); g_task_set_task_data (task, req, (GDestroyNotify)g_usb_device_req_free); + if (g_task_return_error_if_cancelled (task)) { + g_object_unref (task); + return; + } + /* munge back to flags */ if (direction == G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST) request_type_raw |= 0x80; @@ -1307,6 +1303,15 @@ g_usb_device_control_transfer_async (GUsbDevice *device, g_task_return_error (task, error); g_object_unref (task); } + + /* setup cancellation after submission */ + if (cancellable != NULL) { + req->cancellable = g_object_ref (cancellable); + req->cancellable_id = g_cancellable_connect (req->cancellable, + G_CALLBACK (g_usb_device_cancelled_cb), + req, + NULL); + } } /** @@ -1377,18 +1382,14 @@ g_usb_device_bulk_transfer_async (GUsbDevice *device, req = g_slice_new0 (GcmDeviceReq); req->transfer = libusb_alloc_transfer (0); - /* setup cancellation */ - if (cancellable != NULL) { - req->cancellable = g_object_ref (cancellable); - req->cancellable_id = g_cancellable_connect (req->cancellable, - G_CALLBACK (g_usb_device_cancelled_cb), - req, - NULL); - } - task = g_task_new (device, cancellable, callback, user_data); g_task_set_task_data (task, req, (GDestroyNotify)g_usb_device_req_free); + if (g_task_return_error_if_cancelled (task)) { + g_object_unref (task); + return; + } + /* fill in transfer details */ libusb_fill_bulk_transfer (req->transfer, device->priv->handle, @@ -1406,6 +1407,15 @@ g_usb_device_bulk_transfer_async (GUsbDevice *device, g_task_return_error (task, error); g_object_unref (task); } + + /* setup cancellation after submission */ + if (cancellable != NULL) { + req->cancellable = g_object_ref (cancellable); + req->cancellable_id = g_cancellable_connect (req->cancellable, + G_CALLBACK (g_usb_device_cancelled_cb), + req, + NULL); + } } /** @@ -1476,18 +1486,14 @@ g_usb_device_interrupt_transfer_async (GUsbDevice *device, req = g_slice_new0 (GcmDeviceReq); req->transfer = libusb_alloc_transfer (0); - /* setup cancellation */ - if (cancellable != NULL) { - req->cancellable = g_object_ref (cancellable); - req->cancellable_id = g_cancellable_connect (req->cancellable, - G_CALLBACK (g_usb_device_cancelled_cb), - req, - NULL); - } - task = g_task_new (device, cancellable, callback, user_data); g_task_set_task_data (task, req, (GDestroyNotify)g_usb_device_req_free); + if (g_task_return_error_if_cancelled (task)) { + g_object_unref (task); + return; + } + /* fill in transfer details */ libusb_fill_interrupt_transfer (req->transfer, device->priv->handle, @@ -1505,6 +1511,15 @@ g_usb_device_interrupt_transfer_async (GUsbDevice *device, g_task_return_error (task, error); g_object_unref (task); } + + /* setup cancellation after submission */ + if (cancellable != NULL) { + req->cancellable = g_object_ref (cancellable); + req->cancellable_id = g_cancellable_connect (req->cancellable, + G_CALLBACK (g_usb_device_cancelled_cb), + req, + NULL); + } } /** |