Skip to content

Live progress + PUTTIMERESULT interim reporting for Flutter FFI#66

Open
olsemeno wants to merge 9 commits into
mainfrom
feature/flutter-ffi
Open

Live progress + PUTTIMERESULT interim reporting for Flutter FFI#66
olsemeno wants to merge 9 commits into
mainfrom
feature/flutter-ffi

Conversation

@olsemeno

Copy link
Copy Markdown
Contributor

Adds a live-progress channel and interim upload reporting so the crate can drive a live Flutter UI via FFI (see the new specure/nettest-flutter-lib repo).

Changes

  • client/live.rs: shared LiveState + per-thread LiveSink for live graphs.
  • runner / run_measurement_with_progress: reports phase, scalar results (ping/jitter/loss/dl/ul) and per-thread samples live; tolerates phase failures gracefully (replaced fatal .unwrap() with soft handling + join().ok()), preventing panics/barrier deadlocks on network hiccups.
  • PUTTIMERESULT interval: PUTTIMERESULT {chunk} {interval_ms} — server emits interim TIMERESULT every interval_ms (delta since last emit); client sends the param, parses multiple TIMERESULT lines and drains interim results during the upload so the upload graph grows live. Backward compatible (0/absent = final-only; old server ignores the extra arg).
  • FLUTTER_BUILD.md: build notes for Rust under Flutter.

Compatibility

  • Plain CLI (nettest -c) unchanged: interval defaults to 0 → classic single final TIMERESULT.
  • Verified against a local server: live download+upload graphs, ping/jitter/loss measured.

🤖 Generated with Claude Code

olsemeno and others added 9 commits June 24, 2026 13:18
- client/live.rs: shared LiveState + per-thread LiveSink for live graphs
- run_threads/run_measurement_with_progress: report phase, scalar results
  and per-thread samples live; tolerate phase failures (no panic/deadlock)
- PUTTIMERESULT interval param: server emits interim TIMERESULT every
  {interval_ms}; client sends the param, parses multiple TIMERESULT lines
  and drains interim results during upload so the upload graph grows live
- FLUTTER_BUILD.md: build notes for Rust under Flutter

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
On real networks the upload send blocks (WouldBlock), so draining interim
TIMERESULT only at chunk boundaries lagged badly (upload graph barely grew).
Register READABLE|WRITABLE during the upload send phases and add a readable
handler that drains pending interim results promptly, so the upload graph
grows smoothly over real links (verified against a remote server).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- PUTTIMERESULT interim sampling/emit by time (mid-chunk), not only at chunk
  boundaries, so the upload graph grows as smoothly as download even with
  multi-MB chunks; client drains interim by time during the send loop
- Fix: write_time_result no longer resets read_pos (it could fire mid-chunk and
  desync chunk parsing, hanging the upload phase)
- Configurable per-phase durations from the client (download/upload default 7s,
  jitter/packetloss default 5s): ClientConfig + MeasurementState setter, wired
  into GETTIME, PUTTIMERESULT/PUT is_last, VoipParams.duration_ms, UDP packet
  count, and per-phase timeouts

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…BUILD to English

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…boundary

Mid-chunk SendInterim (reregister in the middle of reading a chunk) was unstable
on real networks and could stretch the upload phase to ~30s or hang it. Revert
to emitting interim TIMERESULT only at a chunk boundary (read_pos == 0), which is
stable, while keeping time-based sampling (push points without IO) so the upload
curve still has enough points. Drop the per-iteration client drain (back to
chunk-boundary drain); keep the read-event drainer and the read_pos fix.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… lines

The line-based receive_time only completed on a line starting with "ACCEPT ",
but older servers reply with binary data followed by the ACCEPT terminal with
no preceding newline, so the terminal was never at a line start and the phase
hung. Detect completion via buffer.ends_with(ACCEPT terminal) (as the original
did) in addition to clean interim TIMERESULT line parsing, and stop consuming
unrecognized (binary) lines so the ends_with check can see the terminal.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- jitter/packetloss default duration 5000 -> 4000 ms
- VoIP receive late-packet window 3000 -> 2000 ms
- UDP tmax 3s -> 1s per direction (OUT+IN -> ~2s total)
- packetloss duration is split across both directions

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Instead of switching the mio interest READABLE<->WRITABLE mid-chunk (which was
unstable on real networks), write the interim TIMERESULT inline on the same
socket — TCP is full-duplex, so no reregister/phase change is needed. Points are
now delivered every ~interval (200ms) regardless of chunk size, giving a smooth
upload curve, while WouldBlock with nothing written simply skips that interim
(retried next cycle) so the receive loop never stalls.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
RMBT spec (phases 2/5) doubles the chunk size *while the total pre-test
duration has not exceeded d* (2s). The client checked per-operation time
instead, so on high-RTT links it kept doubling up to 4MB over ~11 round-trips,
making the "init" phase take up to ~10s. Use the phase start time to cap the
whole pre-test at d. Measured init: ~10s -> ~2s on the dev server.

Co-Authored-By: Claude Opus 4.8 <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.

1 participant