Skip to content

Commit 10cf388

Browse files
committed
quic: add a data structure for tracking lists of sent packets
Store in-flight packets in a ring buffer. For golang/go#58547 Change-Id: I825c4e600bb496c2f8f6c195085aaae3e847445e Reviewed-on: https://go-review.googlesource.com/c/net/+/499285 TryBot-Result: Gopher Robot <[email protected]> Reviewed-by: Jonathan Amsterdam <[email protected]> Run-TryBot: Damien Neil <[email protected]>
1 parent ccc217c commit 10cf388

File tree

2 files changed

+202
-0
lines changed

2 files changed

+202
-0
lines changed

internal/quic/sent_packet_list.go

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
// Copyright 2023 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
//go:build go1.21
6+
7+
package quic
8+
9+
// A sentPacketList is a ring buffer of sentPackets.
10+
//
11+
// Processing an ack for a packet causes all older packets past a small threshold
12+
// to be discarded (RFC 9002, Section 6.1.1), so the list of in-flight packets is
13+
// not sparse and will contain at most a few acked/lost packets we no longer
14+
// care about.
15+
type sentPacketList struct {
16+
nextNum packetNumber // next packet number to add to the buffer
17+
off int // offset of first packet in the buffer
18+
size int // number of packets
19+
p []*sentPacket
20+
}
21+
22+
// start is the first packet in the list.
23+
func (s *sentPacketList) start() packetNumber {
24+
return s.nextNum - packetNumber(s.size)
25+
}
26+
27+
// end is one after the last packet in the list.
28+
// If the list is empty, start == end.
29+
func (s *sentPacketList) end() packetNumber {
30+
return s.nextNum
31+
}
32+
33+
// discard clears the list.
34+
func (s *sentPacketList) discard() {
35+
*s = sentPacketList{}
36+
}
37+
38+
// add appends a packet to the list.
39+
func (s *sentPacketList) add(sent *sentPacket) {
40+
if s.nextNum != sent.num {
41+
panic("inserting out-of-order packet")
42+
}
43+
s.nextNum++
44+
if s.size >= len(s.p) {
45+
s.grow()
46+
}
47+
i := (s.off + s.size) % len(s.p)
48+
s.size++
49+
s.p[i] = sent
50+
}
51+
52+
// nth returns a packet by index.
53+
func (s *sentPacketList) nth(n int) *sentPacket {
54+
index := (s.off + n) % len(s.p)
55+
return s.p[index]
56+
}
57+
58+
// num returns a packet by number.
59+
// It returns nil if the packet is not in the list.
60+
func (s *sentPacketList) num(num packetNumber) *sentPacket {
61+
i := int(num - s.start())
62+
if i < 0 || i >= s.size {
63+
return nil
64+
}
65+
return s.nth(i)
66+
}
67+
68+
// clean removes all acked or lost packets from the head of the list.
69+
func (s *sentPacketList) clean() {
70+
for s.size > 0 {
71+
sent := s.p[s.off]
72+
if !sent.acked && !sent.lost {
73+
return
74+
}
75+
sent.recycle()
76+
s.p[s.off] = nil
77+
s.off = (s.off + 1) % len(s.p)
78+
s.size--
79+
}
80+
s.off = 0
81+
}
82+
83+
// grow increases the buffer to hold more packaets.
84+
func (s *sentPacketList) grow() {
85+
newSize := len(s.p) * 2
86+
if newSize == 0 {
87+
newSize = 64
88+
}
89+
p := make([]*sentPacket, newSize)
90+
for i := 0; i < s.size; i++ {
91+
p[i] = s.nth(i)
92+
}
93+
s.p = p
94+
s.off = 0
95+
}
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
// Copyright 2023 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
//go:build go1.21
6+
7+
package quic
8+
9+
import "testing"
10+
11+
func TestSentPacketListSlidingWindow(t *testing.T) {
12+
// Record 1000 sent packets, acking everything outside the most recent 10.
13+
list := &sentPacketList{}
14+
const window = 10
15+
for i := packetNumber(0); i < 1000; i++ {
16+
list.add(&sentPacket{num: i})
17+
if i < window {
18+
continue
19+
}
20+
prev := i - window
21+
sent := list.num(prev)
22+
if sent == nil {
23+
t.Fatalf("packet %v not in list", prev)
24+
}
25+
if sent.num != prev {
26+
t.Fatalf("list.num(%v) = packet %v", prev, sent.num)
27+
}
28+
if got := list.nth(0); got != sent {
29+
t.Fatalf("list.nth(0) != list.num(%v)", prev)
30+
}
31+
sent.acked = true
32+
list.clean()
33+
if got := list.num(prev); got != nil {
34+
t.Fatalf("list.num(%v) = packet %v, expected it to be discarded", prev, got.num)
35+
}
36+
if got, want := list.start(), prev+1; got != want {
37+
t.Fatalf("list.start() = %v, want %v", got, want)
38+
}
39+
if got, want := list.end(), i+1; got != want {
40+
t.Fatalf("list.end() = %v, want %v", got, want)
41+
}
42+
if got, want := list.size, window; got != want {
43+
t.Fatalf("list.size = %v, want %v", got, want)
44+
}
45+
}
46+
}
47+
48+
func TestSentPacketListGrows(t *testing.T) {
49+
// Record 1000 sent packets.
50+
list := &sentPacketList{}
51+
const count = 1000
52+
for i := packetNumber(0); i < count; i++ {
53+
list.add(&sentPacket{num: i})
54+
}
55+
if got, want := list.start(), packetNumber(0); got != want {
56+
t.Fatalf("list.start() = %v, want %v", got, want)
57+
}
58+
if got, want := list.end(), packetNumber(count); got != want {
59+
t.Fatalf("list.end() = %v, want %v", got, want)
60+
}
61+
if got, want := list.size, count; got != want {
62+
t.Fatalf("list.size = %v, want %v", got, want)
63+
}
64+
for i := packetNumber(0); i < count; i++ {
65+
sent := list.num(i)
66+
if sent == nil {
67+
t.Fatalf("packet %v not in list", i)
68+
}
69+
if sent.num != i {
70+
t.Fatalf("list.num(%v) = packet %v", i, sent.num)
71+
}
72+
if got := list.nth(int(i)); got != sent {
73+
t.Fatalf("list.nth(%v) != list.num(%v)", int(i), i)
74+
}
75+
}
76+
}
77+
78+
func TestSentPacketListCleanAll(t *testing.T) {
79+
list := &sentPacketList{}
80+
// Record 10 sent packets.
81+
const count = 10
82+
for i := packetNumber(0); i < count; i++ {
83+
list.add(&sentPacket{num: i})
84+
}
85+
// Mark all the packets as acked.
86+
for i := packetNumber(0); i < count; i++ {
87+
list.num(i).acked = true
88+
}
89+
list.clean()
90+
if got, want := list.size, 0; got != want {
91+
t.Fatalf("list.size = %v, want %v", got, want)
92+
}
93+
list.add(&sentPacket{num: 10})
94+
if got, want := list.size, 1; got != want {
95+
t.Fatalf("list.size = %v, want %v", got, want)
96+
}
97+
sent := list.num(10)
98+
if sent == nil {
99+
t.Fatalf("packet %v not in list", 10)
100+
}
101+
if sent.num != 10 {
102+
t.Fatalf("list.num(10) = %v", sent.num)
103+
}
104+
if got := list.nth(0); got != sent {
105+
t.Fatalf("list.nth(0) != list.num(10)")
106+
}
107+
}

0 commit comments

Comments
 (0)