code wiki / (root) / nx_quic_recovery.nx

nx_quic_recovery.nx source

↩ module page · 57 lines · 2916 B

1// nx_quic_recovery.nx -- RUNG 7 of the sovereign QUIC transport: loss detection + congestion control 2// (RFC 9002). RTT estimation (sec 5), packet/time-threshold loss detection (sec 6), and NewReno 3// congestion control (sec 7) -- the machinery that decides WHEN a datagram is lost and HOW FAST to send, 4// which is exactly where FEC vs ARQ diverges on the lossy Belarus<->Texas link. All integer (microseconds, 5// bytes) -- no float, the Nishi way. license_tier: ORIGINAL 6import "nx_syscalls.nx" 7 8// ---- RTT estimation (RFC 9002 sec 5). state st[]: [smoothed_rtt, rttvar, min_rtt, has_sample] (microseconds) 9func quic_rtt_init(st: *i64) -> i64 { st[0]=0; st[1]=0; st[2]=0; st[3]=0; return 0 } 10func quic_rtt_sample(st: *i64, latest_rtt: i64, ack_delay: i64) -> i64 { 11 if st[3] == 0 { st[2] = latest_rtt } else { if latest_rtt < st[2] { st[2] = latest_rtt } } 12 if st[3] == 0 { 13 st[0] = latest_rtt // smoothed_rtt = first sample 14 st[1] = latest_rtt / 2 // rttvar = sample/2 15 st[3] = 1 16 return 0 17 } 18 var adjusted: i64 = latest_rtt 19 if latest_rtt >= st[2] + ack_delay { adjusted = latest_rtt - ack_delay } 20 var diff: i64 = st[0] - adjusted; if diff < 0 { diff = 0 - diff } 21 st[1] = (3 * st[1] + diff) / 4 // rttvar = 3/4 rttvar + 1/4 |smoothed-adjusted| 22 st[0] = (7 * st[0] + adjusted) / 8 // smoothed = 7/8 smoothed + 1/8 adjusted 23 return 0 24} 25 26// ---- NewReno congestion control (RFC 9002 sec 7). cc[]: [cwnd, ssthresh] (bytes) 27const QC_MAX_DATAGRAM: i64 = 1200 28const QC_INIT_CWND: i64 = 12000 // min(10*max, max(2*max, 14720)) = min(12000,14720) = 12000 29const QC_SSTHRESH_INF: i64 = 9223372036854775807 30func quic_cc_init(cc: *i64) -> i64 { cc[0] = QC_INIT_CWND; cc[1] = QC_SSTHRESH_INF; return 0 } 31func quic_cc_on_ack(cc: *i64, bytes_acked: i64) -> i64 { 32 if cc[0] < cc[1] { cc[0] = cc[0] + bytes_acked } // slow start 33 else { cc[0] = cc[0] + (QC_MAX_DATAGRAM * bytes_acked) / cc[0] } // congestion avoidance 34 return 0 35} 36func quic_cc_on_loss(cc: *i64) -> i64 { 37 cc[1] = cc[0] / 2 // ssthresh = cwnd * kLossReductionFactor(0.5) 38 cc[0] = cc[1] 39 let minw: i64 = 2 * QC_MAX_DATAGRAM // kMinimumWindow = 2 * max_datagram_size 40 if cc[0] < minw { cc[0] = minw } 41 return 0 42} 43 44// ---- loss detection (RFC 9002 sec 6) 45const QC_PKT_THRESHOLD: i64 = 3 // kPacketThreshold 46func quic_pkt_lost_by_threshold(pn: i64, largest_acked: i64) -> i64 { 47 if largest_acked - pn >= QC_PKT_THRESHOLD { return 1 } 48 return 0 49} 50// time-threshold loss_delay = 9/8 * max(smoothed_rtt, latest_rtt) (kTimeThreshold = 9/8) 51func quic_loss_delay(smoothed_rtt: i64, latest_rtt: i64) -> i64 { 52 var m: i64 = smoothed_rtt 53 if latest_rtt > m { m = latest_rtt } 54 return (9 * m) / 8 55} 56 57func main() -> i64 { return 0 }