Skip to content

Keep sending queued bytes after a +++ escape - #89

Open
andy5995 wants to merge 3 commits into
masterfrom
cmd-flush
Open

Keep sending queued bytes after a +++ escape#89
andy5995 wants to merge 3 commits into
masterfrom
cmd-flush

Conversation

@andy5995

@andy5995 andy5995 commented Aug 6, 2026

Copy link
Copy Markdown
Member

Posted by Claude (Opus 5), an LLM made by Anthropic, at andy5995's direction.

Fixes #85.

Only ONLINE drained the socket write buffer, so leaving that state threw away whatever the DTE had handed over. A +++ escape does not hang up, and those bytes are still owed to the peer.

CMD now polls the socket and flushes it while the carrier is up, and ATO resumes the same call with its buffers intact rather than clearing them the way a new connection has to. Bytes are still dropped once the socket is gone, because nothing can carry them then and m2k_pending_to_line() has to be able to reach zero. ATH and ATZ close the connection from inside CMD without a state transition, so that cleanup runs on each iteration instead of on entry to the state.

The telnet IAC parser state now survives ATO too. That follows from keeping the receive buffer: it can hold bytes from the middle of a sequence, and resetting the parser would misdecode them.

Two commits, in this order:

  • tests: share the slow-peer and no-wait step helpersstep_fast() was byte-identical in two test files and the slow-peer connector differed only in its name. No behaviour change; it builds and passes on its own, so bisect does not land on a broken tree.
  • Keep sending queued bytes after a +++ escape — the fix and its tests.

Worth knowing for review:

  • This deliberately undoes part of bf762ac. That commit discarded the queue on leaving ONLINE, which is what let the pending count reach zero. The count still reaches zero here, by sending rather than by dropping while a peer is there to send to.
  • The issue text is stale. It was drafted before bf762ac landed and describes two of the three paths as stranded; since that commit they were discarded. The defect underneath was unchanged.
  • The issue's closing paragraph is not addressed — the m2k_pending_to_dte() mirror. I do not think there is a defect there. m2k_read_to_app() tests only for app-I/O mode, never for the state, so an embedding host can still drain the DTE-side buffer after M2K_STATE_DONE; in PTY mode DONE means the PTY has closed, so there is no descriptor left to flush to. Reopening is the right move if that reasoning is wrong.
  • The first CI run went red on FreeBSD and OpenBSD, in the test rather than the fix. Both drain loops polled a fixed number of times after m2k_pending_to_line() reached zero, but that only says the bytes left modemu2k — the rest of the trip belongs to the kernel. Fifty polls outlasted loopback delivery on Linux and not on either BSD. An instrumented run shows one poll happening after the queue empties on Linux as well, so the assumption was wrong everywhere and only Linux was fast enough to hide it. The loops now wait on the byte count, folded into the commit that added them.
  • Each of the three new tests was run against a build with its own fix disabled and confirmed to fail there, so none of them passes for an unrelated reason. The same check was repeated after the helpers moved, since that refactor is what would quietly make them inert.

@andy5995 andy5995 added this to the v0.2.5 milestone Aug 6, 2026
andy5995 and others added 3 commits August 6, 2026 02:44
> Body drafted by Claude (Opus 5) at andy5995's direction.

Records how to build dosemu2 against a working-tree libmodemu2k and
run its vmodem tests in the container, including the traps that cost
time: running as root breaks the exec test, docker exec bypasses the
entrypoint's UID remap, and Alpine's ldconfig shim exits 1.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
step_fast() was byte-identical in two test files and the slow-peer
connector differed only in its name. Both move to test_helpers, where
the next test that needs back-pressure can reach them.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
> Body drafted by Claude (Opus 5) at andy5995's direction.

Only ONLINE drained the socket write buffer, so leaving it threw away
whatever the DTE had handed over: a +++ escape does not hang up, and
those bytes are still owed to the peer. CMD now polls the socket and
flushes while the carrier is up, and ATO resumes the same call with
its buffers intact instead of resetting them as if it were a new
connection.

They are still dropped once the socket is gone, since nothing can
carry them then, and m2k_pending_to_line() has to be able to reach
zero. ATH and ATZ close the connection from inside CMD without a state
transition, so that cleanup runs per iteration rather than on entry.

Fixes #85

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.

Queued socket output stranded or discarded when leaving ONLINE

1 participant