1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
|
# RFC 6298's retransmission timer: a sender's segments sent and acknowledged (cumulatively); the round-trip time of each
# acknowledged segment that was never sent again (Karn) updates SRTT, RTTVAR and RTO (2.2-2.5: 1 s at least, 60 s at
# most), and the timer runs as 5.1-5.6 say: started when one is sent and none runs, stopped when all are acknowledged,
# restarted when new data is, and on expiry the earliest is sent again and RTO doubles. Time is the events' now: the
# timer is a deadline in the state, looked at on each tick.
record Segment {
seq = 0
sent = 0
again = false # sent again: no sample from it (Karn)
}
record Retransmit {
seq = 0
at = 0
}
state srtt = 0
state rttvar = 0
state rto = 1000
state measured = false
state deadline = -1 # when the timer expires, or -1: not running
state outstanding: list of Segment
state retransmits: list of Retransmit
input transmit = message { seq: u32 }
input ack = message { seq: u32 }
input tick = every 10 ms
fn LOWEST = 1000; fn HIGHEST = 60000 # milliseconds
on transmit {
outstanding.append(Segment { seq: transmit.seq, sent: now })
if deadline < 0 { deadline = now + rto } # 5.1
}
on ack {
let acked, sent = 0, 0
let again = false
for s in outstanding {
if s.seq <= ack.seq {
acked = acked + 1
sent, again = s.sent, s.again
}
}
if acked > 0 {
for k in 0 .. acked { outstanding.remove(outstanding.first) }
if !again {
let r = now - sent
if !measured { # 2.2
srtt, rttvar, measured = r, r / 2, true
} else { # 2.3
rttvar = 0.75 * rttvar + 0.25 * abs(srtt - r)
srtt = 0.875 * srtt + 0.125 * r
}
rto = min(max(srtt + max(tick.period, 4 * rttvar), LOWEST), HIGHEST) # 2.4, 2.5 (G: the tick's period)
}
deadline = if outstanding.count > 0 { now + rto } else { -1 } # 5.3, 5.2
}
}
on tick {
if deadline >= 0 && now >= deadline { # 5.4-5.6
outstanding[outstanding.first].again = true
retransmits.append(Retransmit { seq: outstanding.first.seq, at: now })
rto = min(rto * 2, HIGHEST)
deadline = now + rto
}
}
|