Repository navigation
Fix MAVLink listener socket leak and missing thread-signal reset - #124
Conversation
app/mavlink/src/main/cpp/mavlink.cpp's listen() never closed its UDP socket on any exit path (bind failure, setsockopt failure, recv error, peer shutdown, or the normal loop exit once nativeStop() sets mavlink_thread_signal). The bound fd leaked for the life of the process, silently soaking up every datagram the kernel delivered to port 14550 even after the reading thread had exited -- so once the parser had been started and stopped once, nothing could ever read that port's traffic again for the rest of the process's life, confirmed via `netstat` showing an ever-growing, undrained receive queue. Separately, mavlink_thread_signal was never reset when starting again, so a second nativeStart() call would see the exit flag already set from a previous nativeStop() and exit its loop before reading a single packet. Neither was reachable through stock PixelPilot's own call pattern (nativeStart()/nativeStop() are each called at most once per app lifetime today: once at onCreate, once on the first backgrounding), so both bugs were latent. They matter for a caller that starts/stops the listener repeatedly during the same process -- verified on-device by toggling the listener on and off multiple times and confirming the receive queue no longer grows unbounded and every restart parses new traffic (rather than exiting immediately). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
PR Summary by QodoClose MAVLink listener sockets and reset the stop signal on restart
AI Description
Diagram
High-Level Assessment
Files changed (1)
|
Code Review by Qodo
1. Quick restarts can stop telemetry
|
|
Code review by qodo was updated up to the latest commit bdf370c |
josephnef
left a comment
There was a problem hiding this comment.
Approving. Every exit path of listen() now closes the socket and nativeStart() resets the exit flag; both are correct and the change is minimal. CI is green on the reopened run.
Two non-blocking notes:
- The listener thread is detached, so a fast stop/start can race: the new thread's
bind()can hitEADDRINUSEwhile the old one is still betweenrecvfromreturning andclose(fd). SettingSO_REUSEADDRbeforebind(or keeping the thread handle and joining innativeStart) would close that gap. Qodo raised the same point. - Worth knowing for the follow-ups: on master
nativeStartis only called fromonCreateandnativeStopfromonStop, so after the first backgrounding the OSD telemetry is dead for the rest of the process anyway. This fix is a prerequisite for anything that restarts the listener, including #125.
app/mavlink/src/main/cpp/mavlink.cpp's listen() never closed its UDP
socket on any exit path (bind failure, setsockopt failure, recv error,
peer shutdown, or the normal loop exit once nativeStop() sets
mavlink_thread_signal). The bound fd leaked for the life of the
process, silently soaking up every datagram the kernel delivered to
port 14550 even after the reading thread had exited -- so once the
parser had been started and stopped once, nothing could ever read that
port's traffic again for the rest of the process's life, confirmed via
netstatshowing an ever-growing, undrained receive queue.Separately, mavlink_thread_signal was never reset when starting again,
so a second nativeStart() call would see the exit flag already set
from a previous nativeStop() and exit its loop before reading a single
packet.
Neither was reachable through stock PixelPilot's own call pattern
(nativeStart()/nativeStop() are each called at most once per app
lifetime today: once at onCreate, once on the first backgrounding), so
both bugs were latent. They matter for a caller that starts/stops the
listener repeatedly during the same process -- verified on-device by
toggling the listener on and off multiple times and confirming the
receive queue no longer grows unbounded and every restart parses new
traffic (rather than exiting immediately).
🤖 Generated with Claude Code