Thank you Steve. For the async to be really watertight we probably cannot assume that all the callbacks are executed directly after the cancels without delay. I have been playing with a variation below that waits for all callbacks to be finished based on counting the number of transfers cancelled. This requires a bit more testing as has more code changes.
For now the short Sleep has solved my issues on Windows. with device close. Thank you for merging it. Kind regards, Jasper Author: jvde.github <jvde.git...@gmail.com> Date: Mon Jan 10 21:40:48 2022 +0100 rework async_wait to ensure all events handled at close diff --git a/src/librtlsdr.c b/src/librtlsdr.c index 2682d77..a4ae212 100644 --- a/src/librtlsdr.c +++ b/src/librtlsdr.c @@ -125,6 +125,7 @@ struct rtlsdr_dev { int dev_lost; int driver_active; unsigned int xfer_errors; + int xfer_cb_cancel_count; }; void rtlsdr_set_gpio_bit(rtlsdr_dev_t *dev, uint8_t gpio, int val); @@ -1706,8 +1707,11 @@ static void LIBUSB_CALL _libusb_callback(struct libusb_transfer *xfer) dev->cb(xfer->buffer, xfer->actual_length, dev->cb_ctx); libusb_submit_transfer(xfer); /* resubmit transfer */ + dev->xfer_errors = 0; - } else if (LIBUSB_TRANSFER_CANCELLED != xfer->status) { + } else if (LIBUSB_TRANSFER_CANCELLED == xfer->status) { + dev->xfer_cb_cancel_count++; + } else { #ifndef _WIN32 if (LIBUSB_TRANSFER_ERROR == xfer->status) dev->xfer_errors++; @@ -1853,9 +1857,9 @@ int rtlsdr_read_async(rtlsdr_dev_t *dev, rtlsdr_read_async_cb_t cb, void *ctx, { unsigned int i; int r = 0; + int ret = 0; + int cancel_count = 0; struct timeval tv = { 1, 0 }; - struct timeval zerotv = { 0, 0 }; - enum rtlsdr_async_status next_status = RTLSDR_INACTIVE; if (!dev) return -1; @@ -1865,6 +1869,7 @@ int rtlsdr_read_async(rtlsdr_dev_t *dev, rtlsdr_read_async_cb_t cb, void *ctx, dev->async_status = RTLSDR_RUNNING; dev->async_cancel = 0; + dev->xfer_cb_cancel_count = 0; /* number of cancelled transfers */ dev->cb = cb; dev->cb_ctx = ctx; @@ -1881,6 +1886,7 @@ int rtlsdr_read_async(rtlsdr_dev_t *dev, rtlsdr_read_async_cb_t cb, void *ctx, _rtlsdr_alloc_async_buffers(dev); + /* creating initial submits */ for(i = 0; i < dev->xfer_buf_num; ++i) { libusb_fill_bulk_transfer(dev->xfer[i], dev->devh, @@ -1890,7 +1896,6 @@ int rtlsdr_read_async(rtlsdr_dev_t *dev, rtlsdr_read_async_cb_t cb, void *ctx, _libusb_callback, (void *)dev, BULK_TIMEOUT); - r = libusb_submit_transfer(dev->xfer[i]); if (r < 0) { fprintf(stderr, "Failed to submit transfer %i\n" @@ -1900,62 +1905,44 @@ int rtlsdr_read_async(rtlsdr_dev_t *dev, rtlsdr_read_async_cb_t cb, void *ctx, "echo 0 > /sys/module/usbcore" "/parameters/usbfs_memory_mb\n", i); dev->async_status = RTLSDR_CANCELING; + ret = r; break; } } - while (RTLSDR_INACTIVE != dev->async_status) { - r = libusb_handle_events_timeout_completed(dev->ctx, &tv, - &dev->async_cancel); + /* handling events until device is cancelled */ + while (RTLSDR_CANCELING != dev->async_status) { + r = libusb_handle_events_timeout_completed(dev->ctx, &tv, &dev->async_cancel); + if (r < 0) { - /*fprintf(stderr, "handle_events returned: %d\n", r);*/ + if (r == LIBUSB_ERROR_INTERRUPTED) /* stray signal */ continue; + ret = r; break; } + } - if (RTLSDR_CANCELING == dev->async_status) { - next_status = RTLSDR_INACTIVE; - - if (!dev->xfer) - break; - - for(i = 0; i < dev->xfer_buf_num; ++i) { - if (!dev->xfer[i]) - continue; - - if (LIBUSB_TRANSFER_CANCELLED != - dev->xfer[i]->status) { - r = libusb_cancel_transfer(dev->xfer[i]); - /* handle events after canceling - * to allow transfer status to - * propagate */ -#ifdef _WIN32 - Sleep(1); -#endif - libusb_handle_events_timeout_completed(dev->ctx, - &zerotv, NULL); - if (r < 0) - continue; + /* cancel and count all transfers */ + for(i = 0; i < dev->xfer_buf_num; ++i) { - next_status = RTLSDR_CANCELING; - } - } + if(dev->xfer[i]) { - if (dev->dev_lost || RTLSDR_INACTIVE == next_status) { - /* handle any events that still need to - * be handled before exiting after we - * just cancelled all transfers */ - libusb_handle_events_timeout_completed(dev->ctx, - &zerotv, NULL); - break; - } + r = libusb_cancel_transfer(dev->xfer[i]); + if (r == 0) cancel_count++; } } + /* handle events but expect at least "cancelled" CB calls with LIBUSB_TRANSFER_CANCELLED */ + do + { + libusb_handle_events_timeout(dev->ctx, &tv); + } + while (dev->xfer_cb_cancel_count != cancel_count); + _rtlsdr_free_async_buffers(dev); - dev->async_status = next_status; + dev->async_status = RTLSDR_INACTIVE; return r; }