Skip to content

Put a deadline on remote file and memory transfers - #40

Open
kfox wants to merge 1 commit into
SensoriumEmbedded:mainfrom
kfox:fw-transfer-deadlines
Open

kfox wants to merge 1 commit into
SensoriumEmbedded:mainfrom
kfox:fw-transfer-deadlines

Conversation

@kfox

@kfox kfox commented Sep 27, 2026 •

Copy link
Copy Markdown
Contributor

Split out of #37 and independent of the extension work. Based on main.

The problem. A transfer's only bound was the per-byte timeout. A peer trickling one byte just inside it held loop(), and with it the C64 menu, for as long as it liked. That is 65535 × 500 mS (9.1 hours) for a WriteC64Mem, and longer for a file.

The fix.

  • An overall deadline. Receiving a file, sending a file, and WriteC64Mem now stop at TransferCeilingmS(len). That is a floor, plus the length at a rate well under what each channel really does. They answer FailToken with how far they got. The deadline is per channel because a single floor would fail real uploads: USBHostSerial at 115200 8N1 moves 11.52 bytes/mS at the wire.
  • Draining after a failed receive. DrainCmdChannel discards what the peer is still sending, and is itself bounded. Without it, the rest of the payload was read as the next command. A side effect: a command sent within about a second of a per-byte timeout is drained with the rest.
  • Non-blocking flushes. The flushes on these paths go through FlushCmdChannel, which does not block. EthernetClient::flush() and the USB host serial's flush() wait for the peer to acknowledge, with no deadline.

Merging alongside the others

Measured on this branch

  • On a TR+ in an NTSC C64, flashed through the SD updater with the build stamp confirmed afterwards:
    • Trickle: a 1000-byte post to SD over USB, one byte every 300 mS. After 18 bytes (about 5.4 s) the board answered FailToken with Too slow, 18 of 1000 bytes, and it answered a version request afterwards. The 18 bytes stay on the card as a partial file, as a per-byte timeout leaves one today.
    • Full speed: a 4 MB post landed with size 4000000.
    • A DMA keypress still reaches the menu.

Not measured: the same trickle against main as a control, and the Ethernet and USB-host channels.

A transfer's only bound was the per-byte timeout, so a peer trickling one
byte just inside it held loop() -- and with it the C64 menu -- for as long as
it liked: 65535 x 500 mS for a WriteC64Mem, and longer for a file. Receiving
and sending a file and WriteC64Mem now stop at TransferCeilingmS(len), a floor
plus the length at a rate well under what each channel really does, and
answer FailToken with how far they got.

After a failed receive, DrainCmdChannel discards what the peer is still
sending, bounded itself, so the rest of the payload is not read as the next
command. And the flushes on these paths go through FlushCmdChannel, which
does not block: EthernetClient::flush() and the USB host serial's flush wait
with no deadline for a peer to acknowledge.
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.

1 participant