summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenjamin Berg <bberg@redhat.com>2020-09-30 23:34:07 +0200
committerBenjamin Berg <bberg@redhat.com>2020-10-01 00:02:08 +0200
commite3ce22315687529768dc1a3b407f4bcca956bd09 (patch)
tree643791dfa27e98cd04342e468d1d7d0e0e2441b9
parent9ce9a6c99be107bc11a5fa2bd28a58e54fc27958 (diff)
downloadgusb-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.c69
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);
+ }
}
/**