Context
Design: docs/plans/2026-03-19-channel-based-io-driver-design.md — Phase 2/3
Problem: Server has global Mutex<HashMap<ConnectionId, QuicConnection>>. ALL connections contend on ONE lock. stream_send() locks connections + batched_sender. run() loop also needs connections for handle_packet + flush_all_batched.
Solution
Per-connection mpsc channels for stream_send. run() loop remains UDP socket owner and packet demuxer. Per-connection flush tasks drain channels.
Changes Required
crates/quick-transport/src/server.rs
- Add per-connection channels:
conn_channels: HashMap<ConnectionId, mpsc::Sender<ConnCommand>>
- Each new connection gets its own bounded channel
- stream_send() routes through per-connection channel (no global lock)
- run() loop demuxes packets, calls conn.recv() with brief per-connection lock
- Per-connection task handles send commands + flush
bins/quick-exit/src/tunnel.rs
- Adapt to new server.stream_send() API (should be transparent — same signature)
Acceptance Criteria
Dependencies
Requires #116 (client I/O driver) validated first.
Expected Impact
Symmetrical improvement for exit→entry (download) direction. Combined with #116: 75 Mbps terrestrial target.
Context
Design: docs/plans/2026-03-19-channel-based-io-driver-design.md — Phase 2/3
Problem: Server has global Mutex<HashMap<ConnectionId, QuicConnection>>. ALL connections contend on ONE lock. stream_send() locks connections + batched_sender. run() loop also needs connections for handle_packet + flush_all_batched.
Solution
Per-connection mpsc channels for stream_send. run() loop remains UDP socket owner and packet demuxer. Per-connection flush tasks drain channels.
Changes Required
crates/quick-transport/src/server.rs
conn_channels: HashMap<ConnectionId, mpsc::Sender<ConnCommand>>bins/quick-exit/src/tunnel.rs
Acceptance Criteria
Dependencies
Requires #116 (client I/O driver) validated first.
Expected Impact
Symmetrical improvement for exit→entry (download) direction. Combined with #116: 75 Mbps terrestrial target.