Two Qodo threads on #417 were closed without a change when the review cycle was stopped (maintainer decision; merged under skip-qodo-gate). Both are corner cases of the pipelined-write failure paths in UsbTransport, reachable only with two adapters sharing one libusb context in one process, which no demo does today.
-
Completion counter published before the free-list handoff. async_write_cb increments pool->completed before the mutex-guarded free-list push. A taker on the batch thread that wakes on the counter can find the free list still empty and wait out one more turn. Cost is latency, not correctness. Fix shape: increment completed inside the same critical section as the push, or wake on the free list instead of the counter.
-
write_errors reset before the generation bump. write_batch_begin clears write_errors and then increments generation. A late callback from a slot of the previous generation that runs between the two stores still compares equal to the old generation and can count into the fresh batch. Fix shape: bump the generation first, then reset the counter.
Also recorded as a deliberate design decision, not a follow-up: a pipelined transfer still submitted after the destructor's cancel + bounded reap is leaked with an error rather than freed or waited on forever (a dead libusb event loop is the only way to get there).
Context and measurements: #417 body; src/UsbTransport.cpp comments at the sites.
Two Qodo threads on #417 were closed without a change when the review cycle was stopped (maintainer decision; merged under
skip-qodo-gate). Both are corner cases of the pipelined-write failure paths inUsbTransport, reachable only with two adapters sharing one libusb context in one process, which no demo does today.Completion counter published before the free-list handoff.
async_write_cbincrementspool->completedbefore the mutex-guarded free-list push. A taker on the batch thread that wakes on the counter can find the free list still empty and wait out one more turn. Cost is latency, not correctness. Fix shape: incrementcompletedinside the same critical section as the push, or wake on the free list instead of the counter.write_errorsreset before the generation bump.write_batch_beginclearswrite_errorsand then incrementsgeneration. A late callback from a slot of the previous generation that runs between the two stores still compares equal to the old generation and can count into the fresh batch. Fix shape: bump the generation first, then reset the counter.Also recorded as a deliberate design decision, not a follow-up: a pipelined transfer still submitted after the destructor's cancel + bounded reap is leaked with an error rather than freed or waited on forever (a dead libusb event loop is the only way to get there).
Context and measurements: #417 body;
src/UsbTransport.cppcomments at the sites.