Fix MAVLink listener socket leak and missing thread-signal reset - #124
Fix MAVLink listener socket leak and missing thread-signal reset#124mengelh wants to merge 1 commit into
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 QodoFix MAVLink listener socket cleanup and restart state
AI Description
Diagram
High-Level Assessment
Files changed (1)
|
Code Review by Qodo
1. Quick restarts can stop telemetry
|
| // it has to be cleared here or a restart (e.g. toggling streaming mode off | ||
| // again) would see it already set and exit its loop before ever reading a | ||
| // packet. | ||
| mavlink_thread_signal = 0; |
There was a problem hiding this comment.
1. Quick restarts can stop telemetry 🐞 Bug ☼ Reliability
nativeStart clears mavlink_thread_signal and launches another detached listener without waiting for the previous listener to close its socket. If the old thread has observed the stop signal but has not yet closed the descriptor, the new thread can fail to bind before the old one exits, leaving no thread receiving telemetry.
Agent Prompt
## Issue description
A restart can reset the shared stop signal and launch a new listener before the previous detached listener has closed its bound socket. Retain and synchronize ownership of the listener thread so starting waits for prior shutdown, and use an atomic or otherwise synchronized stop state.
## Issue Context
The listener may take up to the receive timeout to observe a stop request. The replacement thread can therefore encounter the still-bound port and exit immediately, followed by the previous thread closing its socket and leaving no active listener.
## Fix Focus Areas
- app/mavlink/src/main/cpp/mavlink.cpp[57-57]
- app/mavlink/src/main/cpp/mavlink.cpp[85-104]
- app/mavlink/src/main/cpp/mavlink.cpp[322-324]
- app/mavlink/src/main/cpp/mavlink.cpp[382-397]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
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