Skip to content

Bugfix: Ignore libusb callback scheduling after the asyncio loop is closed#947

Merged
barbibulle merged 2 commits into
google:mainfrom
greateggsgreg:safe-libusb-callback-scheduling
Jun 27, 2026
Merged

Bugfix: Ignore libusb callback scheduling after the asyncio loop is closed#947
barbibulle merged 2 commits into
google:mainfrom
greateggsgreg:safe-libusb-callback-scheduling

Conversation

@greateggsgreg

Copy link
Copy Markdown
Contributor

Summary

During an unclean USB transport shutdown, the asyncio loop can be closed while a USB transfer is still in flight. When libusb later invokes the transfer completion callback on its C callback thread, Bumble calls loop.call_soon_threadsafe(...) on the closed loop. That raises RuntimeError('Event loop is closed') on the C callback thread and can escalate
inside libusb to a mutex assertion and process abort (SIGABRT).

To reproduce: Start a USB HCI transport, leave an IN or OUT transfer in flight, and close the asyncio loop before libusb delivers the transfer callback. Without this fix, the late callback raises RuntimeError('Event loop is closed') on the libusb callback thread and can abort the process. With this fix, the late callback is ignored.

Root Cause

The libusb event thread schedules back onto the asyncio loop at seven sites. Before this fix, each site called loop.call_soon_threadsafe(...) directly, so a callback that arrived after loop close raised on the callback thread.

The fix adds _safe_call_soon() at bumble/transport/usb.py:41- bumble/transport/usb.py:51, which catches the closed-loop RuntimeError and turns late callbacks into no-ops. All libusb-event-thread scheduling sites now use it.

@barbibulle barbibulle merged commit fe991f6 into google:main Jun 27, 2026
60 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants