Skip to content

Commit a233290

Browse files
committed
quic: add a data structure for tracking sent packets
When we send a packet, we need to remember its contents until it has been acked or detected as lost. For golang/go#58547 Change-Id: I8c18f7ca1730a3ce460cd562d060dd6c7cfa9ffb Reviewed-on: https://go-review.googlesource.com/c/net/+/495236 Reviewed-by: Jonathan Amsterdam <jba@google.com> Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com> Run-TryBot: Damien Neil <dneil@google.com> TryBot-Result: Gopher Robot <gobot@golang.org>
1 parent 61d852e commit a233290

File tree

2 files changed

+151
-0
lines changed

2 files changed

+151
-0
lines changed

internal/quic/sent_packet.go

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
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+
package quic
6+
7+
import (
8+
"sync"
9+
"time"
10+
)
11+
12+
// A sentPacket tracks state related to an in-flight packet we sent,
13+
// to be committed when the peer acks it or resent if the packet is lost.
14+
type sentPacket struct {
15+
num packetNumber
16+
size int // size in bytes
17+
time time.Time // time sent
18+
19+
ackEliciting bool // https://www.rfc-editor.org/rfc/rfc9002.html#section-2-3.4.1
20+
inFlight bool // https://www.rfc-editor.org/rfc/rfc9002.html#section-2-3.6.1
21+
acked bool // ack has been received
22+
lost bool // packet is presumed lost
23+
24+
// Frames sent in the packet.
25+
//
26+
// This is an abbreviated version of the packet payload, containing only the information
27+
// we need to process an ack for or loss of this packet.
28+
// For example, a CRYPTO frame is recorded as the frame type (0x06), offset, and length,
29+
// but does not include the sent data.
30+
b []byte
31+
n int // read offset into b
32+
}
33+
34+
var sentPool = sync.Pool{
35+
New: func() interface{} {
36+
return &sentPacket{}
37+
},
38+
}
39+
40+
func newSentPacket() *sentPacket {
41+
sent := sentPool.Get().(*sentPacket)
42+
sent.reset()
43+
return sent
44+
}
45+
46+
// recycle returns a sentPacket to the pool.
47+
func (sent *sentPacket) recycle() {
48+
sentPool.Put(sent)
49+
}
50+
51+
func (sent *sentPacket) reset() {
52+
*sent = sentPacket{
53+
b: sent.b[:0],
54+
}
55+
}
56+
57+
// The append* methods record information about frames in the packet.
58+
59+
func (sent *sentPacket) appendNonAckElicitingFrame(frameType byte) {
60+
sent.b = append(sent.b, frameType)
61+
}
62+
63+
func (sent *sentPacket) appendAckElicitingFrame(frameType byte) {
64+
sent.ackEliciting = true
65+
sent.inFlight = true
66+
sent.b = append(sent.b, frameType)
67+
}
68+
69+
func (sent *sentPacket) appendInt(v uint64) {
70+
sent.b = appendVarint(sent.b, v)
71+
}
72+
73+
func (sent *sentPacket) appendOffAndSize(start int64, size int) {
74+
sent.b = appendVarint(sent.b, uint64(start))
75+
sent.b = appendVarint(sent.b, uint64(size))
76+
}
77+
78+
// The next* methods read back information about frames in the packet.
79+
80+
func (sent *sentPacket) next() (frameType byte) {
81+
f := sent.b[sent.n]
82+
sent.n++
83+
return f
84+
}
85+
86+
func (sent *sentPacket) nextInt() uint64 {
87+
v, n := consumeVarint(sent.b[sent.n:])
88+
sent.n += n
89+
return v
90+
}
91+
92+
func (sent *sentPacket) nextRange() (start, end int64) {
93+
start = int64(sent.nextInt())
94+
end = start + int64(sent.nextInt())
95+
return start, end
96+
}
97+
98+
func (sent *sentPacket) done() bool {
99+
return sent.n == len(sent.b)
100+
}

internal/quic/sent_packet_test.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
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+
package quic
6+
7+
import "testing"
8+
9+
func TestSentPacket(t *testing.T) {
10+
frames := []interface{}{
11+
byte(frameTypePing),
12+
byte(frameTypeStreamBase),
13+
uint64(1),
14+
i64range{1 << 20, 1<<20 + 1024},
15+
}
16+
// Record sent frames.
17+
sent := newSentPacket()
18+
for _, f := range frames {
19+
switch f := f.(type) {
20+
case byte:
21+
sent.appendAckElicitingFrame(f)
22+
case uint64:
23+
sent.appendInt(f)
24+
case i64range:
25+
sent.appendOffAndSize(f.start, int(f.size()))
26+
}
27+
}
28+
// Read the record.
29+
for i, want := range frames {
30+
if done := sent.done(); done {
31+
t.Fatalf("before consuming contents, sent.done() = true, want false")
32+
}
33+
switch want := want.(type) {
34+
case byte:
35+
if got := sent.next(); got != want {
36+
t.Fatalf("%v: sent.next() = %v, want %v", i, got, want)
37+
}
38+
case uint64:
39+
if got := sent.nextInt(); got != want {
40+
t.Fatalf("%v: sent.nextInt() = %v, want %v", i, got, want)
41+
}
42+
case i64range:
43+
if start, end := sent.nextRange(); start != want.start || end != want.end {
44+
t.Fatalf("%v: sent.nextRange() = [%v,%v), want %v", i, start, end, want)
45+
}
46+
}
47+
}
48+
if done := sent.done(); !done {
49+
t.Fatalf("after consuming contents, sent.done() = false, want true")
50+
}
51+
}

0 commit comments

Comments
 (0)