aboutsummaryrefslogtreecommitdiffstats
path: root/research/experiments/044-specs/rto.py
diff options
context:
space:
mode:
Diffstat (limited to 'research/experiments/044-specs/rto.py')
-rw-r--r--research/experiments/044-specs/rto.py40
1 files changed, 40 insertions, 0 deletions
diff --git a/research/experiments/044-specs/rto.py b/research/experiments/044-specs/rto.py
new file mode 100644
index 0000000..e4a4a57
--- /dev/null
+++ b/research/experiments/044-specs/rto.py
@@ -0,0 +1,40 @@
+# RFC 6298's retransmission timer in plain Python: the reference 140 counts aup's lines against. A sender's segments
+# are sent and acknowledged (cumulatively) at given times; 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. The timer is seen at ticks of period P, as an app sees time.
+LOWEST, HIGHEST = 1000, 60000 # milliseconds; the clock's granularity G is the tick's period
+
+
+class Sender:
+ def __init__(self, period):
+ self.period, self.srtt, self.rttvar, self.rto, self.measured = period, 0.0, 0.0, 1000.0, False
+ self.deadline, self.outstanding, self.retransmits = -1.0, [], [] # outstanding: [seq, sent, again]
+
+ def send(self, now, seq):
+ self.outstanding.append([seq, now, False])
+ if self.deadline < 0:
+ self.deadline = now + self.rto # 5.1
+
+ def ack(self, now, seq):
+ acked = [s for s in self.outstanding if s[0] <= seq]
+ if not acked:
+ return
+ self.outstanding = [s for s in self.outstanding if s[0] > seq]
+ last = acked[-1]
+ if not last[2]: # Karn: no sample from a segment sent again
+ r = now - last[1]
+ if not self.measured:
+ self.srtt, self.rttvar, self.measured = r, r / 2, True # 2.2
+ else:
+ self.rttvar = 0.75 * self.rttvar + 0.25 * abs(self.srtt - r) # 2.3
+ self.srtt = 0.875 * self.srtt + 0.125 * r
+ self.rto = min(max(self.srtt + max(self.period, 4 * self.rttvar), LOWEST), HIGHEST) # 2.4, 2.5
+ self.deadline = now + self.rto if self.outstanding else -1.0 # 5.3, 5.2
+
+ def tick(self, now):
+ if self.deadline >= 0 and now >= self.deadline: # 5.4-5.6
+ self.outstanding[0][2] = True
+ self.retransmits.append((self.outstanding[0][0], now))
+ self.rto = min(self.rto * 2, HIGHEST)
+ self.deadline = now + self.rto