mirror of https://github.com/ospab/ostp.git
pacing_rate had been computed on every ACK since the controller was written and never read by anything: admission was decided purely by cwnd. But cwnd bounds how much may be UNACKNOWLEDGED, not how fast it reaches the wire, so a full window went out back-to-back. On a bottleneck with a deep buffer that burst is absorbed rather than dropped, and it lands as standing queue — the mechanism behind the multi-second RTT this protocol has been showing on mobile. It is also why BBR could not simply be dropped in: BBR's whole model is "send at the estimated bottleneck rate", which is meaningless without a pacer underneath it. Adds a token bucket to CongestionController, charged in on_send so every byte that reaches the wire pays exactly once — retransmits included, since those are precisely what must not bypass the limit and pile into a full queue. Burst allowance is 10ms-at-rate rather than one packet. Pacing intervals here are fractions of a millisecond, so strict per-packet release would need a sub-millisecond timer per packet; sizing the burst to the loop's existing ~10ms wakeup lets the configured rate still be saturated. There is a floor of 4 MTU so a cold or collapsed rate estimate can never wedge sending entirely. Wired into both directions. The client gates its proxy-event branch on it, and the server's per-session backpressure snapshot reports zero headroom when the bucket is empty. The download path matters most here — that is the one carrying video — and it was also still clamped to the old 16384-packet ceiling (~20 MB outstanding), now aligned with MAX_CWND_PACKETS. Tested that the bucket denies once drained and refills over time; the second is what keeps a stalled bucket from wedging sending permanently. |
||
|---|---|---|
| .. | ||
| src | ||
| Cargo.toml | ||