-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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>
- Loading branch information
Showing
2 changed files
with
151 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
// Copyright 2023 The Go Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style | ||
// license that can be found in the LICENSE file. | ||
|
||
package quic | ||
|
||
import ( | ||
"sync" | ||
"time" | ||
) | ||
|
||
// A sentPacket tracks state related to an in-flight packet we sent, | ||
// to be committed when the peer acks it or resent if the packet is lost. | ||
type sentPacket struct { | ||
num packetNumber | ||
size int // size in bytes | ||
time time.Time // time sent | ||
|
||
ackEliciting bool // https://www.rfc-editor.org/rfc/rfc9002.html#section-2-3.4.1 | ||
inFlight bool // https://www.rfc-editor.org/rfc/rfc9002.html#section-2-3.6.1 | ||
acked bool // ack has been received | ||
lost bool // packet is presumed lost | ||
|
||
// Frames sent in the packet. | ||
// | ||
// This is an abbreviated version of the packet payload, containing only the information | ||
// we need to process an ack for or loss of this packet. | ||
// For example, a CRYPTO frame is recorded as the frame type (0x06), offset, and length, | ||
// but does not include the sent data. | ||
b []byte | ||
n int // read offset into b | ||
} | ||
|
||
var sentPool = sync.Pool{ | ||
New: func() interface{} { | ||
return &sentPacket{} | ||
}, | ||
} | ||
|
||
func newSentPacket() *sentPacket { | ||
sent := sentPool.Get().(*sentPacket) | ||
sent.reset() | ||
return sent | ||
} | ||
|
||
// recycle returns a sentPacket to the pool. | ||
func (sent *sentPacket) recycle() { | ||
sentPool.Put(sent) | ||
} | ||
|
||
func (sent *sentPacket) reset() { | ||
*sent = sentPacket{ | ||
b: sent.b[:0], | ||
} | ||
} | ||
|
||
// The append* methods record information about frames in the packet. | ||
|
||
func (sent *sentPacket) appendNonAckElicitingFrame(frameType byte) { | ||
sent.b = append(sent.b, frameType) | ||
} | ||
|
||
func (sent *sentPacket) appendAckElicitingFrame(frameType byte) { | ||
sent.ackEliciting = true | ||
sent.inFlight = true | ||
sent.b = append(sent.b, frameType) | ||
} | ||
|
||
func (sent *sentPacket) appendInt(v uint64) { | ||
sent.b = appendVarint(sent.b, v) | ||
} | ||
|
||
func (sent *sentPacket) appendOffAndSize(start int64, size int) { | ||
sent.b = appendVarint(sent.b, uint64(start)) | ||
sent.b = appendVarint(sent.b, uint64(size)) | ||
} | ||
|
||
// The next* methods read back information about frames in the packet. | ||
|
||
func (sent *sentPacket) next() (frameType byte) { | ||
f := sent.b[sent.n] | ||
sent.n++ | ||
return f | ||
} | ||
|
||
func (sent *sentPacket) nextInt() uint64 { | ||
v, n := consumeVarint(sent.b[sent.n:]) | ||
sent.n += n | ||
return v | ||
} | ||
|
||
func (sent *sentPacket) nextRange() (start, end int64) { | ||
start = int64(sent.nextInt()) | ||
end = start + int64(sent.nextInt()) | ||
return start, end | ||
} | ||
|
||
func (sent *sentPacket) done() bool { | ||
return sent.n == len(sent.b) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
// Copyright 2023 The Go Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style | ||
// license that can be found in the LICENSE file. | ||
|
||
package quic | ||
|
||
import "testing" | ||
|
||
func TestSentPacket(t *testing.T) { | ||
frames := []interface{}{ | ||
byte(frameTypePing), | ||
byte(frameTypeStreamBase), | ||
uint64(1), | ||
i64range{1 << 20, 1<<20 + 1024}, | ||
} | ||
// Record sent frames. | ||
sent := newSentPacket() | ||
for _, f := range frames { | ||
switch f := f.(type) { | ||
case byte: | ||
sent.appendAckElicitingFrame(f) | ||
case uint64: | ||
sent.appendInt(f) | ||
case i64range: | ||
sent.appendOffAndSize(f.start, int(f.size())) | ||
} | ||
} | ||
// Read the record. | ||
for i, want := range frames { | ||
if done := sent.done(); done { | ||
t.Fatalf("before consuming contents, sent.done() = true, want false") | ||
} | ||
switch want := want.(type) { | ||
case byte: | ||
if got := sent.next(); got != want { | ||
t.Fatalf("%v: sent.next() = %v, want %v", i, got, want) | ||
} | ||
case uint64: | ||
if got := sent.nextInt(); got != want { | ||
t.Fatalf("%v: sent.nextInt() = %v, want %v", i, got, want) | ||
} | ||
case i64range: | ||
if start, end := sent.nextRange(); start != want.start || end != want.end { | ||
t.Fatalf("%v: sent.nextRange() = [%v,%v), want %v", i, start, end, want) | ||
} | ||
} | ||
} | ||
if done := sent.done(); !done { | ||
t.Fatalf("after consuming contents, sent.done() = false, want true") | ||
} | ||
} |