Hello, I'm submitting a patch that fixes a critical issue where applications using rtlsdr_read_async() hang indefinitely when USB transfer errors occur.
## Background I encountered this issue while running long-term monitoring with rtl_fm for FLEX paging signals. When the RTL-SDR dongle would shift slightly in the USB socket (causing USB transfer errors), rtl_fm would hang indefinitely instead of exiting cleanly. ## Root Cause The problem occurs in two parts: 1. The _libusb_callback() function calls rtlsdr_cancel_async() from within the libusb event handler callback. This creates a race condition where dev->async_cancel is modified while libusb_handle_events_timeout_completed() is actively using it as a parameter, leading to unpredictable behavior. 2. Even when the dev->dev_lost flag is set, the async read loop doesn't properly exit, and applications like rtl_fm have no way to detect that the device is lost. ## Solution The attached patch: - Removes the problematic rtlsdr_cancel_async() call from the callback - Adds detection of dev->dev_lost in the main event loop to properly transition to RTLSDR_CANCELING state - Makes rtlsdr_read_async() return -1 when the device is lost - Updates rtl_fm to check the return value and terminate gracefully ## Testing Tested with RTL2832U dongle (FC0012 tuner) by deliberately triggering USB errors through physical movement. The applications now terminate cleanly with an appropriate error message instead of hanging indefinitely. Normal operation (Ctrl+C termination) continues to work correctly. This fix makes rtl-sdr more robust for long-running monitoring applications and deployment scenarios where physical interference might occur (e.g., Raspberry Pi installations). Please review and consider merging. Best regards, Ramon Smits
From 151ca8b33ec559ff407ac9b3d1bbd8fceb80ba50 Mon Sep 17 00:00:00 2001 From: Ramon Smits <[email protected]> Date: Sat, 10 Jan 2026 20:50:25 +0100 Subject: [PATCH] Fix application hang on USB transfer errors When USB transfer errors occur (e.g., dongle movement in socket), applications using rtlsdr_read_async() would hang indefinitely instead of terminating gracefully. The issue had two parts: 1. _libusb_callback() was calling rtlsdr_cancel_async() from within the libusb event handler, creating a race condition by modifying dev->async_cancel while libusb_handle_events_timeout_completed() was actively using it as a parameter. 2. Even when dev->dev_lost was set, the async read loop would not properly exit, and applications like rtl_fm had no way to detect the device loss. This patch: - Removes the problematic rtlsdr_cancel_async() call from the callback - Adds proper dev->dev_lost detection in the main event loop to transition to RTLSDR_CANCELING state - Returns -1 from rtlsdr_read_async() when device is lost - Updates rtl_fm to check the return value and terminate gracefully with an error message Tested with RTL2832U/FC0012 dongle by deliberately triggering USB errors (dongle movement). Applications now terminate cleanly instead of hanging indefinitely. Signed-off-by: Ramon Smits <[email protected]> --- src/librtlsdr.c | 9 ++++++++- src/rtl_fm.c | 6 +++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/librtlsdr.c b/src/librtlsdr.c index ee13556..c9e43a1 100644 --- a/src/librtlsdr.c +++ b/src/librtlsdr.c @@ -1732,7 +1732,6 @@ static void LIBUSB_CALL _libusb_callback(struct libusb_transfer *xfer) LIBUSB_TRANSFER_NO_DEVICE == xfer->status) { #endif dev->dev_lost = 1; - rtlsdr_cancel_async(dev); fprintf(stderr, "cb transfer status: %d, " "canceling...\n", xfer->status); #ifndef _WIN32 @@ -1930,6 +1929,11 @@ int rtlsdr_read_async(rtlsdr_dev_t *dev, rtlsdr_read_async_cb_t cb, void *ctx, break; } + /* Check if device was lost due to transfer errors */ + if (dev->dev_lost && RTLSDR_RUNNING == dev->async_status) { + dev->async_status = RTLSDR_CANCELING; + } + if (RTLSDR_CANCELING == dev->async_status) { next_status = RTLSDR_INACTIVE; @@ -1973,6 +1977,9 @@ int rtlsdr_read_async(rtlsdr_dev_t *dev, rtlsdr_read_async_cb_t cb, void *ctx, dev->async_status = next_status; + if (dev->dev_lost) + return -1; + return r; } diff --git a/src/rtl_fm.c b/src/rtl_fm.c index 0929744..976887b 100644 --- a/src/rtl_fm.c +++ b/src/rtl_fm.c @@ -809,7 +809,11 @@ static void rtlsdr_callback(unsigned char *buf, uint32_t len, void *ctx) static void *dongle_thread_fn(void *arg) { struct dongle_state *s = arg; - rtlsdr_read_async(s->dev, rtlsdr_callback, s, 0, s->buf_len); + int r = rtlsdr_read_async(s->dev, rtlsdr_callback, s, 0, s->buf_len); + if (r != 0 && !do_exit) { + fprintf(stderr, "\nDevice error detected, async read returned: %d\n", r); + do_exit = 1; + } return 0; } -- 2.52.0
