Lock::lock(timeout) on Portduino now honours its deadline: poll
pthread_mutex_trylock to the deadline, since pthread_mutex_timedlock is not
portable to macOS, and return false rather than blocking for good. A zero
timeout no longer waits at all.
DMShell keeps the bytes a nonblocking PTY would not take. write() on a
nonblocking fd may accept less than it was given, and the receive cursor has
already moved past those bytes, so the tail is queued and retried ahead of
any later input rather than being dropped.
A killed shell child is now reaped. SIGKILL ends the process but does not
collect it, so the pid is kept until waitpid() confirms the exit instead of
being cleared straight after the signal.
PING replay uses the highest cursor the peer reported, as
notePeerReceiveCursor() already does. A stale last_rx_seq beside a higher
ack_seq asked for a replay from a frame the history had dropped, which ended
a session the peer was perfectly happy with.
A sequenced frame that never reaches the air gives its number back. The
callers draw from nextTxSeq before sendFrameToPeer(), so a failed packet
allocation or encode left a hole no replay could fill. The encode path also
leaked the packet it had allocated.
In the client, the config handshake now applies --timeout to each blocking
read, so a peer that never answers no longer hangs it for good, and a
non-TTY stdin waits for its queued input to be acknowledged before it sends
CLOSE - the server handles CLOSE ahead of any gap, so piped input longer
than the window was being truncated in silence.
Co-Authored-By: Jonathan Bennett <jbennett@incomsystems.biz>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VeY5PczVYsxWNZhE2Cx4Sv