fix(api): bound the stream drain so a full config dump can't starve the watchdog (#11164)

writeStream() drained the whole queue in one call - "send every packet we can".
A client asking for the full config gets the node database, then the file
manifest, then the packet backlog and the position replay, and none of that
returns to loop(). On a full node database (120 entries) the dump runs past
eight seconds, so on RP2350, where rp2040Loop() arms an 8s hardware watchdog
and is the only thing that calls watchdog_update(), the board resets in the
middle of the manifest. Reproducible on every connection; with a small node
database the dump finished under the timeout and nothing looked wrong.

Measured on a pico2_w5500_e22: last loop iteration at millis=27242, ServerAPI
kept logging until uptime 35s, reset at 35.2s = 27.242 + 8.0.

Take a slice instead. The PhoneAPI state machine is resumable, so writeStream()
stops after STREAM_WRITE_BUDGET_MSEC and reports whether anything is left;
runOncePart() then asks to be re-run immediately rather than sleeping out
readStream's idle delay, so the dump keeps its throughput while loop() gets to
feed the watchdog between slices. Backpressure on a retained frame still
returns the normal delay - re-running at once would just spin on a full
transport.

Verified on hardware: 10/10 full config dumps against a node with 120 entries,
no resets, dump still completes in ~8s.
This commit is contained in:
Carlos Valdes
2026-08-04 14:14:08 +00:00
committed by GitHub
parent 7302db1672
commit d89f3eb8fc
2 changed files with 33 additions and 23 deletions
+7 -4
View File
@@ -9,6 +9,10 @@
// A To/FromRadio packet + our 32 bit header
#define MAX_STREAM_BUF_SIZE (MAX_TO_FROM_RADIO_SIZE + sizeof(uint32_t))
// Cap on one writeStream() slice: an uncapped dump never reaches loop(), so a board with a
// hardware watchdog (RP2350 arms 8s) resets mid-dump.
#define STREAM_WRITE_BUDGET_MSEC 100
/**
* A version of our 'phone' API that talks over a Stream. So therefore well suited to use with serial links
* or TCP connections.
@@ -64,10 +68,9 @@ class StreamAPI : public PhoneAPI
int32_t readStream(const char *buf, uint16_t bufLen);
int32_t handleRecStream(const char *buf, uint16_t bufLen);
/**
* call getFromRadio() and deliver encapsulated packets to the Stream
*/
void writeStream();
/// Emit a slice of pending output. True asks the caller to come straight back; false covers
/// both a drained queue and backpressure, so it does not mean the queue is empty.
bool writeStream();
protected:
/**