Hi all,

As per discussion with Martin Ling on this mailing list, please find below a 
proposed patch that simplifies the async_read,  keeps track of open tranfers 
and checks for zero open transfers before closing to avoid unidentified 
behaviour at close.

Thanks, Jasper

commit 4b06ae321e91296f13355a0715e9a961b4f57e45
Author: jvde.github <jvde.git...@gmail.com>
Date:   Mon Jan 24 20:15:34 2022 +0100

    count live transfers before close

diff --git a/src/librtlsdr.c b/src/librtlsdr.c
index 096abae..67e30c4 100644
--- a/src/librtlsdr.c
+++ b/src/librtlsdr.c
@@ -119,6 +119,7 @@ struct rtlsdr_dev {
        int dev_lost;
        int driver_active;
        unsigned int xfer_errors;
+       int live_transfers;
 };
 
 void rtlsdr_set_gpio_bit(rtlsdr_dev_t *dev, uint8_t gpio, int val);
@@ -1695,11 +1696,15 @@ static void LIBUSB_CALL _libusb_callback(struct 
libusb_transfer *xfer)
 {
        rtlsdr_dev_t *dev = (rtlsdr_dev_t *)xfer->user_data;
 
+       dev->live_transfers --;
+
        if (LIBUSB_TRANSFER_COMPLETED == xfer->status) {
                if (dev->cb)
                        dev->cb(xfer->buffer, xfer->actual_length, dev->cb_ctx);
 
-               libusb_submit_transfer(xfer); /* resubmit transfer */
+               if(!dev->async_cancel)
+                       if( libusb_submit_transfer(xfer) == 0) /* resubmit 
transfer */
+                               dev->live_transfers++;
                dev->xfer_errors = 0;
        } else if (LIBUSB_TRANSFER_CANCELLED != xfer->status) {
 #ifndef _WIN32
@@ -1847,9 +1852,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;
        struct timeval tv = { 1, 0 };
-       struct timeval zerotv = { 0, 0 };
-       enum rtlsdr_async_status next_status = RTLSDR_INACTIVE;
+       struct timeval canceltv = { 0, 50 };
 
        if (!dev)
                return -1;
@@ -1863,6 +1868,7 @@ int rtlsdr_read_async(rtlsdr_dev_t *dev, 
rtlsdr_read_async_cb_t cb, void *ctx,
        dev->cb = cb;
        dev->cb_ctx = ctx;
 
+
        if (buf_num > 0)
                dev->xfer_buf_num = buf_num;
        else
@@ -1875,6 +1881,8 @@ int rtlsdr_read_async(rtlsdr_dev_t *dev, 
rtlsdr_read_async_cb_t cb, void *ctx,
 
        _rtlsdr_alloc_async_buffers(dev);
 
+       dev->live_transfers = 0;
+
        for(i = 0; i < dev->xfer_buf_num; ++i) {
                libusb_fill_bulk_transfer(dev->xfer[i],
                                          dev->devh,
@@ -1894,64 +1902,41 @@ 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 = -3;
                        break;
                }
+               else
+                       dev->live_transfers++;
        }
 
-       while (RTLSDR_INACTIVE != dev->async_status) {
+       while (RTLSDR_RUNNING == dev->async_status) {
                r = libusb_handle_events_timeout_completed(dev->ctx, &tv,
-                                                          &dev->async_cancel);
+                       &dev->async_cancel);
                if (r < 0) {
                        /*fprintf(stderr, "handle_events returned: %d\n", r);*/
                        if (r == LIBUSB_ERROR_INTERRUPTED) /* stray signal */
                                continue;
-                       break;
+                       rtlsdr_cancel_async(dev);
+                       ret = -4;
                }
+       }
 
-               if (RTLSDR_CANCELING == dev->async_status) {
-                       next_status = RTLSDR_INACTIVE;
+       if (!dev->xfer) return -5;
 
-                       if (!dev->xfer)
-                               break;
+       for(i = 0; i < dev->xfer_buf_num; ++i)
+               if (dev->xfer[i])
+                       libusb_cancel_transfer(dev->xfer[i]);
 
-                       for(i = 0; i < dev->xfer_buf_num; ++i) {
-                               if (!dev->xfer[i])
-                                       continue;
+       for (i = 0; i < dev->xfer_buf_num && dev->live_transfers > 0; ++i)
+               libusb_handle_events_timeout_completed(dev->ctx, &canceltv, 
NULL);
 
-                               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;
-
-                                       next_status = RTLSDR_CANCELING;
-                               }
-                       }
-
-                       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;
-                       }
-               }
-       }
+       if(dev->live_transfers > 0) return -6;
 
        _rtlsdr_free_async_buffers(dev);
 
-       dev->async_status = next_status;
+       dev->async_status = RTLSDR_INACTIVE;
 
-       return r;
+       return ret;
 }
 
 int rtlsdr_cancel_async(rtlsdr_dev_t *dev)

Reply via email to