-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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 type tracking sent values
Any given datum communicated to the peer follows a state machine: - We do not need to send the this datum. - We need to send it, but have not done so. - We have sent it, but the peer has not acknowledged it. - We have sent it and the peer has acknowledged it. Data transitions between states in a consistent fashion; for example, loss of the most recent packet containing a HANDSHAKE_DONE frame means we should resend the frame in a new packet. Add a sentVal type which tracks this state machine. For golang/go#58547 Change-Id: I9de0ef5e482534b8733ef66363bac8f6c0fd3173 Reviewed-on: https://go-review.googlesource.com/c/net/+/498295 Run-TryBot: Damien Neil <[email protected]> Reviewed-by: Jonathan Amsterdam <[email protected]> Auto-Submit: Damien Neil <[email protected]> TryBot-Result: Gopher Robot <[email protected]>
- Loading branch information
Showing
2 changed files
with
269 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,103 @@ | ||
// 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 | ||
|
||
// A sentVal tracks sending some piece of information to the peer. | ||
// It tracks whether the information has been sent, acked, and | ||
// (when in-flight) the most recent packet to carry it. | ||
// | ||
// For example, a sentVal can track sending of a RESET_STREAM frame. | ||
// | ||
// - unset: stream is active, no need to send RESET_STREAM | ||
// - unsent: we should send a RESET_STREAM, but have not yet | ||
// - sent: we have sent a RESET_STREAM, but have not received an ack | ||
// - received: we have sent a RESET_STREAM, and the peer has acked the packet that contained it | ||
// | ||
// In the "sent" state, a sentVal also tracks the latest packet number to carry | ||
// the information. (QUIC packet numbers are always at most 62 bits in size, | ||
// so the sentVal keeps the number in the low 62 bits and the state in the high 2 bits.) | ||
type sentVal uint64 | ||
|
||
const ( | ||
sentValUnset = 0 // unset | ||
sentValUnsent = 1 << 62 // set, not sent to the peer | ||
sentValSent = 2 << 62 // set, sent to the peer but not yet acked; pnum is set | ||
sentValReceived = 3 << 62 // set, peer acked receipt | ||
|
||
sentValStateMask = 3 << 62 | ||
) | ||
|
||
// isSet reports whether the value is set. | ||
func (s sentVal) isSet() bool { return s != 0 } | ||
|
||
// shouldSend reports whether the value is set and has not been sent to the peer. | ||
func (s sentVal) shouldSend() bool { return s.state() == sentValUnsent } | ||
|
||
// shouldSend reports whether the the value needs to be sent to the peer. | ||
// The value needs to be sent if it is set and has not been sent. | ||
// If pto is true, indicating that we are sending a PTO probe, the value | ||
// should also be sent if it is set and has not been acknowledged. | ||
func (s sentVal) shouldSendPTO(pto bool) bool { | ||
st := s.state() | ||
return st == sentValUnsent || (pto && st == sentValSent) | ||
} | ||
|
||
// isReceived reports whether the value has been received by the peer. | ||
func (s sentVal) isReceived() bool { return s == sentValReceived } | ||
|
||
// set sets the value and records that it should be sent to the peer. | ||
// If the value has already been sent, it is not resent. | ||
func (s *sentVal) set() { | ||
if *s == 0 { | ||
*s = sentValUnsent | ||
} | ||
} | ||
|
||
// reset sets the value to the unsent state. | ||
func (s *sentVal) setUnsent() { *s = sentValUnsent } | ||
|
||
// clear sets the value to the unset state. | ||
func (s *sentVal) clear() { *s = sentValUnset } | ||
|
||
// setSent sets the value to the send state and records the number of the most recent | ||
// packet containing the value. | ||
func (s *sentVal) setSent(pnum packetNumber) { | ||
*s = sentValSent | sentVal(pnum) | ||
} | ||
|
||
// setReceived sets the value to the received state. | ||
func (s *sentVal) setReceived() { *s = sentValReceived } | ||
|
||
// ackOrLoss reports that an acknowledgement has been received for the value, | ||
// or (if acked is false) that the packet carrying the value has been lost. | ||
func (s *sentVal) ackOrLoss(pnum packetNumber, acked bool) { | ||
if acked { | ||
*s = sentValReceived | ||
} else if *s == sentVal(pnum)|sentValSent { | ||
*s = sentValUnsent | ||
} | ||
} | ||
|
||
// ackLatestOrLoss reports that an acknowledgement has been received for the value, | ||
// or (if acked is false) that the packet carrying the value has been lost. | ||
// The value is set to the acked state only if pnum is the latest packet containing it. | ||
// | ||
// We use this to handle acks for data that varies every time it is sent. | ||
// For example, if we send a MAX_DATA frame followed by an updated MAX_DATA value in a | ||
// second packet, we consider the data sent only upon receiving an ack for the most | ||
// recent value. | ||
func (s *sentVal) ackLatestOrLoss(pnum packetNumber, acked bool) { | ||
if acked { | ||
if *s == sentVal(pnum)|sentValSent { | ||
*s = sentValReceived | ||
} | ||
} else { | ||
if *s == sentVal(pnum)|sentValSent { | ||
*s = sentValUnsent | ||
} | ||
} | ||
} | ||
|
||
func (s sentVal) state() uint64 { return uint64(s) & sentValStateMask } |
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,166 @@ | ||
// 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 TestSentVal(t *testing.T) { | ||
for _, test := range []struct { | ||
name string | ||
f func(*sentVal) | ||
wantIsSet bool | ||
wantShouldSend bool | ||
wantIsReceived bool | ||
wantShouldSendPTO bool | ||
}{{ | ||
name: "zero value", | ||
f: func(*sentVal) {}, | ||
wantIsSet: false, | ||
wantShouldSend: false, | ||
wantShouldSendPTO: false, | ||
wantIsReceived: false, | ||
}, { | ||
name: "v.set()", | ||
f: (*sentVal).set, | ||
wantIsSet: true, | ||
wantShouldSend: true, | ||
wantShouldSendPTO: true, | ||
wantIsReceived: false, | ||
}, { | ||
name: "v.setSent(0)", | ||
f: func(v *sentVal) { | ||
v.setSent(0) | ||
}, | ||
wantIsSet: true, | ||
wantShouldSend: false, | ||
wantShouldSendPTO: true, | ||
wantIsReceived: false, | ||
}, { | ||
name: "sent.set()", | ||
f: func(v *sentVal) { | ||
v.setSent(0) | ||
v.set() | ||
}, | ||
wantIsSet: true, | ||
wantShouldSend: false, | ||
wantShouldSendPTO: true, | ||
wantIsReceived: false, | ||
}, { | ||
name: "sent.setUnsent()", | ||
f: func(v *sentVal) { | ||
v.setSent(0) | ||
v.setUnsent() | ||
}, | ||
wantIsSet: true, | ||
wantShouldSend: true, | ||
wantShouldSendPTO: true, | ||
wantIsReceived: false, | ||
}, { | ||
name: "set.clear()", | ||
f: func(v *sentVal) { | ||
v.set() | ||
v.clear() | ||
}, | ||
wantIsSet: false, | ||
wantShouldSend: false, | ||
wantShouldSendPTO: false, | ||
wantIsReceived: false, | ||
}, { | ||
name: "v.setReceived()", | ||
f: (*sentVal).setReceived, | ||
wantIsSet: true, | ||
wantShouldSend: false, | ||
wantShouldSendPTO: false, | ||
wantIsReceived: true, | ||
}, { | ||
name: "v.ackOrLoss(!pnum, true)", | ||
f: func(v *sentVal) { | ||
v.setSent(1) | ||
v.ackOrLoss(0, true) // ack different packet containing the val | ||
}, | ||
wantIsSet: true, | ||
wantShouldSend: false, | ||
wantShouldSendPTO: false, | ||
wantIsReceived: true, | ||
}, { | ||
name: "v.ackOrLoss(!pnum, false)", | ||
f: func(v *sentVal) { | ||
v.setSent(1) | ||
v.ackOrLoss(0, false) // lose different packet containing the val | ||
}, | ||
wantIsSet: true, | ||
wantShouldSend: false, | ||
wantShouldSendPTO: true, | ||
wantIsReceived: false, | ||
}, { | ||
name: "v.ackOrLoss(pnum, false)", | ||
f: func(v *sentVal) { | ||
v.setSent(1) | ||
v.ackOrLoss(1, false) // lose same packet containing the val | ||
}, | ||
wantIsSet: true, | ||
wantShouldSend: true, | ||
wantShouldSendPTO: true, | ||
wantIsReceived: false, | ||
}, { | ||
name: "v.ackLatestOrLoss(!pnum, true)", | ||
f: func(v *sentVal) { | ||
v.setSent(1) | ||
v.ackLatestOrLoss(0, true) // ack different packet containing the val | ||
}, | ||
wantIsSet: true, | ||
wantShouldSend: false, | ||
wantShouldSendPTO: true, | ||
wantIsReceived: false, | ||
}, { | ||
name: "v.ackLatestOrLoss(pnum, true)", | ||
f: func(v *sentVal) { | ||
v.setSent(1) | ||
v.ackLatestOrLoss(1, true) // ack same packet containing the val | ||
}, | ||
wantIsSet: true, | ||
wantShouldSend: false, | ||
wantShouldSendPTO: false, | ||
wantIsReceived: true, | ||
}, { | ||
name: "v.ackLatestOrLoss(!pnum, false)", | ||
f: func(v *sentVal) { | ||
v.setSent(1) | ||
v.ackLatestOrLoss(0, false) // lose different packet containing the val | ||
}, | ||
wantIsSet: true, | ||
wantShouldSend: false, | ||
wantShouldSendPTO: true, | ||
wantIsReceived: false, | ||
}, { | ||
name: "v.ackLatestOrLoss(pnum, false)", | ||
f: func(v *sentVal) { | ||
v.setSent(1) | ||
v.ackLatestOrLoss(1, false) // lose same packet containing the val | ||
}, | ||
wantIsSet: true, | ||
wantShouldSend: true, | ||
wantShouldSendPTO: true, | ||
wantIsReceived: false, | ||
}} { | ||
var v sentVal | ||
test.f(&v) | ||
if got, want := v.isSet(), test.wantIsSet; got != want { | ||
t.Errorf("%v: v.isSet() = %v, want %v", test.name, got, want) | ||
} | ||
if got, want := v.shouldSend(), test.wantShouldSend; got != want { | ||
t.Errorf("%v: v.shouldSend() = %v, want %v", test.name, got, want) | ||
} | ||
if got, want := v.shouldSendPTO(false), test.wantShouldSend; got != want { | ||
t.Errorf("%v: v.shouldSendPTO(false) = %v, want %v", test.name, got, want) | ||
} | ||
if got, want := v.shouldSendPTO(true), test.wantShouldSendPTO; got != want { | ||
t.Errorf("%v: v.shouldSendPTO(true) = %v, want %v", test.name, got, want) | ||
} | ||
if got, want := v.isReceived(), test.wantIsReceived; got != want { | ||
t.Errorf("%v: v.isReceived() = %v, want %v", test.name, got, want) | ||
} | ||
} | ||
} |