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;
 }

Reply via email to