tcp.Policy: add newTransmitLimit output

This commit is contained in:
soypat
2026-09-06 12:29:20 -07:00
parent 04f6294d9c
commit 53b606a1ff
4 changed files with 49 additions and 33 deletions
+8 -6
View File
@@ -184,15 +184,17 @@ func (r *Timer) postRx(incoming tcp.Segment, now int64) {
// PreTx reports whether the retransmission timer has expired and, if so, applies
// the RFC 6298 §5.4–§5.6 timeout response — discard the outstanding RTT sample
// (Karn), back the RTO off exponentially and restart the timer — and asks the
// connection to retransmit from snd.UNA (go-back-N). It writes no TCP options:
// retransmission timing needs none of its own. It implements [tcp.Policy].
func (r *Timer) PreTx(h *tcp.Handler, outgoingOpts tcp.Frame) (rtxFrom tcp.Value, retransmit, holdNew bool) {
// connection to retransmit from snd.UNA (go-back-N). It writes no TCP options
// and imposes no transmit limit: retransmission timing needs neither, and
// congestion control belongs to a Policy composing this timer. It implements
// [tcp.Policy].
func (r *Timer) PreTx(h *tcp.Handler, outgoingOpts tcp.Frame) (newTransmitLimit tcp.Size, rtxFrom tcp.Value, retransmit bool) {
return r.preTx(r.nanotime(), h.ControlBlock().SendUNA())
}
func (r *Timer) preTx(now int64, una tcp.Value) (rtxFrom tcp.Value, retransmit, holdNew bool) {
func (r *Timer) preTx(now int64, una tcp.Value) (newTransmitLimit tcp.Size, rtxFrom tcp.Value, retransmit bool) {
if !r.running || now < r.deadline || r.sndUNA == r.sndNXT {
return 0, false, false
return tcp.TransmitUnlimited, 0, false
}
r.expirations++
r.timing = false // §5.4: do not sample a retransmitted segment.
@@ -202,7 +204,7 @@ func (r *Timer) preTx(now int64, una tcp.Value) (rtxFrom tcp.Value, retransmit,
}
r.running = true
r.deadline = now + int64(r.CurrentRTO())
return una, true, false
return tcp.TransmitUnlimited, una, true
}
// PostTx records an emitted segment: it advances the shadow send sequence,
+5 -5
View File
@@ -106,15 +106,15 @@ func TestRTO_RetransmitOnTimeout(t *testing.T) {
const iss = uint32(1000)
r.postTx(dataSeg(iss, 100), 0)
if _, rtx, _ := r.preTx(int64(rtoInitial)-1, tcp.Value(iss)); rtx {
if _, _, rtx := r.preTx(int64(rtoInitial)-1, tcp.Value(iss)); rtx {
t.Fatal("must not retransmit before the deadline")
}
from, rtx, hold := r.preTx(int64(rtoInitial), tcp.Value(iss))
limit, from, rtx := r.preTx(int64(rtoInitial), tcp.Value(iss))
if !rtx {
t.Fatal("RTO must fire at the deadline with data outstanding")
}
if hold {
t.Error("the estimator never holds new data back")
if limit != tcp.TransmitUnlimited {
t.Error("the estimator never limits new data")
}
if from != tcp.Value(iss) {
t.Errorf("retransmit from %d, want snd.UNA=%d", from, iss)
@@ -293,7 +293,7 @@ func TestRTO_RetransmitsZeroWindowProbe(t *testing.T) {
now := int64(rtoInitial)
prevRTO := r.CurrentRTO()
for attempt := 1; attempt <= 4; attempt++ {
from, rtx, _ := r.preTx(now, tcp.Value(iss))
_, from, rtx := r.preTx(now, tcp.Value(iss))
if !rtx {
t.Fatalf("attempt %d: timer did not fire; the probe would never be resent", attempt)
}