forked from quic-go/quic-go
Merge pull request #2218 from lucas-clemente/pto-per-pn-space
change PTO to be per packet number space
This commit is contained in:
@@ -25,7 +25,9 @@ type packetNumberSpace struct {
|
||||
history *sentPacketHistory
|
||||
pns *packetNumberGenerator
|
||||
|
||||
lossTime time.Time
|
||||
lossTime time.Time
|
||||
lastSentAckElicitingPacketTime time.Time
|
||||
|
||||
largestAcked protocol.PacketNumber
|
||||
largestSent protocol.PacketNumber
|
||||
}
|
||||
@@ -40,15 +42,14 @@ func newPacketNumberSpace(initialPN protocol.PacketNumber) *packetNumberSpace {
|
||||
}
|
||||
|
||||
type sentPacketHandler struct {
|
||||
lastSentAckElicitingPacketTime time.Time // only applies to the application-data packet number space
|
||||
lastSentCryptoPacketTime time.Time
|
||||
|
||||
nextSendTime time.Time
|
||||
|
||||
initialPackets *packetNumberSpace
|
||||
handshakePackets *packetNumberSpace
|
||||
oneRTTPackets *packetNumberSpace
|
||||
|
||||
handshakeComplete bool
|
||||
|
||||
// lowestNotConfirmedAcked is the lowest packet number that we sent an ACK for, but haven't received confirmation, that this ACK actually arrived
|
||||
// example: we send an ACK for packets 90-100 with packet number 20
|
||||
// once we receive an ACK from the peer for packet 20, the lowestNotConfirmedAcked is 101
|
||||
@@ -62,6 +63,7 @@ type sentPacketHandler struct {
|
||||
|
||||
// The number of times a PTO has been sent without receiving an ack.
|
||||
ptoCount uint32
|
||||
ptoMode SendMode
|
||||
// The number of PTO probe packets that should be sent.
|
||||
// Only applies to the application-data packet number space.
|
||||
numProbesToSend int
|
||||
@@ -153,10 +155,7 @@ func (h *sentPacketHandler) sentPacketImpl(packet *Packet) bool /* is ack-elicit
|
||||
isAckEliciting := len(packet.Frames) > 0
|
||||
|
||||
if isAckEliciting {
|
||||
if packet.EncryptionLevel != protocol.Encryption1RTT {
|
||||
h.lastSentCryptoPacketTime = packet.SendTime
|
||||
}
|
||||
h.lastSentAckElicitingPacketTime = packet.SendTime
|
||||
pnSpace.lastSentAckElicitingPacketTime = packet.SendTime
|
||||
packet.includedInBytesInFlight = true
|
||||
h.bytesInFlight += packet.Length
|
||||
if h.numProbesToSend > 0 {
|
||||
@@ -281,7 +280,7 @@ func (h *sentPacketHandler) determineNewlyAckedPackets(
|
||||
return ackedPackets, err
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) getEarliestLossTime() (time.Time, protocol.EncryptionLevel) {
|
||||
func (h *sentPacketHandler) getEarliestLossTimeAndSpace() (time.Time, protocol.EncryptionLevel) {
|
||||
var encLevel protocol.EncryptionLevel
|
||||
var lossTime time.Time
|
||||
|
||||
@@ -293,13 +292,35 @@ func (h *sentPacketHandler) getEarliestLossTime() (time.Time, protocol.Encryptio
|
||||
lossTime = h.handshakePackets.lossTime
|
||||
encLevel = protocol.EncryptionHandshake
|
||||
}
|
||||
if lossTime.IsZero() || (!h.oneRTTPackets.lossTime.IsZero() && h.oneRTTPackets.lossTime.Before(lossTime)) {
|
||||
if h.handshakeComplete &&
|
||||
(lossTime.IsZero() || (!h.oneRTTPackets.lossTime.IsZero() && h.oneRTTPackets.lossTime.Before(lossTime))) {
|
||||
lossTime = h.oneRTTPackets.lossTime
|
||||
encLevel = protocol.Encryption1RTT
|
||||
}
|
||||
return lossTime, encLevel
|
||||
}
|
||||
|
||||
// same logic as getEarliestLossTimeAndSpace, but for lastSentAckElicitingPacketTime instead of lossTime
|
||||
func (h *sentPacketHandler) getEarliestSentTimeAndSpace() (time.Time, protocol.EncryptionLevel) {
|
||||
var encLevel protocol.EncryptionLevel
|
||||
var sentTime time.Time
|
||||
|
||||
if h.initialPackets != nil {
|
||||
sentTime = h.initialPackets.lastSentAckElicitingPacketTime
|
||||
encLevel = protocol.EncryptionInitial
|
||||
}
|
||||
if h.handshakePackets != nil && (sentTime.IsZero() || (!h.handshakePackets.lastSentAckElicitingPacketTime.IsZero() && h.handshakePackets.lastSentAckElicitingPacketTime.Before(sentTime))) {
|
||||
sentTime = h.handshakePackets.lastSentAckElicitingPacketTime
|
||||
encLevel = protocol.EncryptionHandshake
|
||||
}
|
||||
if h.handshakeComplete &&
|
||||
(sentTime.IsZero() || (!h.oneRTTPackets.lastSentAckElicitingPacketTime.IsZero() && h.oneRTTPackets.lastSentAckElicitingPacketTime.Before(sentTime))) {
|
||||
sentTime = h.oneRTTPackets.lastSentAckElicitingPacketTime
|
||||
encLevel = protocol.Encryption1RTT
|
||||
}
|
||||
return sentTime, encLevel
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) hasOutstandingCryptoPackets() bool {
|
||||
var hasInitial, hasHandshake bool
|
||||
if h.initialPackets != nil {
|
||||
@@ -312,11 +333,14 @@ func (h *sentPacketHandler) hasOutstandingCryptoPackets() bool {
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) hasOutstandingPackets() bool {
|
||||
return h.oneRTTPackets.history.HasOutstandingPackets() || h.hasOutstandingCryptoPackets()
|
||||
// We only send application data probe packets once the handshake completes,
|
||||
// because before that, we don't have the keys to decrypt ACKs sent in 1-RTT packets.
|
||||
return (h.handshakeComplete && h.oneRTTPackets.history.HasOutstandingPackets()) ||
|
||||
h.hasOutstandingCryptoPackets()
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) setLossDetectionTimer() {
|
||||
if lossTime, _ := h.getEarliestLossTime(); !lossTime.IsZero() {
|
||||
if lossTime, _ := h.getEarliestLossTimeAndSpace(); !lossTime.IsZero() {
|
||||
// Early retransmit timer or time loss detection.
|
||||
h.alarm = lossTime
|
||||
}
|
||||
@@ -329,7 +353,8 @@ func (h *sentPacketHandler) setLossDetectionTimer() {
|
||||
}
|
||||
|
||||
// PTO alarm
|
||||
h.alarm = h.lastSentAckElicitingPacketTime.Add(h.rttStats.PTO() << h.ptoCount)
|
||||
sentTime, encLevel := h.getEarliestSentTimeAndSpace()
|
||||
h.alarm = sentTime.Add(h.rttStats.PTO(encLevel == protocol.Encryption1RTT) << h.ptoCount)
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) detectLostPackets(
|
||||
@@ -405,7 +430,7 @@ func (h *sentPacketHandler) detectLostPackets(
|
||||
|
||||
func (h *sentPacketHandler) OnLossDetectionTimeout() error {
|
||||
// When all outstanding are acknowledged, the alarm is canceled in
|
||||
// updateLossDetectionAlarm. This doesn't reset the timer in the session though.
|
||||
// setLossDetectionTimer. This doesn't reset the timer in the session though.
|
||||
// When OnAlarm is called, we therefore need to make sure that there are
|
||||
// actually packets outstanding.
|
||||
if h.hasOutstandingPackets() {
|
||||
@@ -418,10 +443,10 @@ func (h *sentPacketHandler) OnLossDetectionTimeout() error {
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) onVerifiedLossDetectionTimeout() error {
|
||||
lossTime, encLevel := h.getEarliestLossTime()
|
||||
if !lossTime.IsZero() {
|
||||
earliestLossTime, encLevel := h.getEarliestLossTimeAndSpace()
|
||||
if !earliestLossTime.IsZero() {
|
||||
if h.logger.Debug() {
|
||||
h.logger.Debugf("Loss detection alarm fired in loss timer mode. Loss time: %s", lossTime)
|
||||
h.logger.Debugf("Loss detection alarm fired in loss timer mode. Loss time: %s", earliestLossTime)
|
||||
}
|
||||
// Early retransmit or time loss detection
|
||||
return h.detectLostPackets(time.Now(), encLevel, h.bytesInFlight)
|
||||
@@ -429,10 +454,21 @@ func (h *sentPacketHandler) onVerifiedLossDetectionTimeout() error {
|
||||
|
||||
// PTO
|
||||
if h.logger.Debug() {
|
||||
h.logger.Debugf("Loss detection alarm fired in PTO mode. PTO count: %d", h.ptoCount)
|
||||
h.logger.Debugf("Loss detection alarm for %s fired in PTO mode. PTO count: %d", encLevel, h.ptoCount)
|
||||
}
|
||||
_, encLevel = h.getEarliestSentTimeAndSpace()
|
||||
h.ptoCount++
|
||||
h.numProbesToSend += 2
|
||||
switch encLevel {
|
||||
case protocol.EncryptionInitial:
|
||||
h.ptoMode = SendPTOInitial
|
||||
case protocol.EncryptionHandshake:
|
||||
h.ptoMode = SendPTOHandshake
|
||||
case protocol.Encryption1RTT:
|
||||
h.ptoMode = SendPTOAppData
|
||||
default:
|
||||
return fmt.Errorf("TPO timer in unexpected encryption level: %s", encLevel)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -495,7 +531,7 @@ func (h *sentPacketHandler) SendMode() SendMode {
|
||||
return SendNone
|
||||
}
|
||||
if h.numProbesToSend > 0 {
|
||||
return SendPTO
|
||||
return h.ptoMode
|
||||
}
|
||||
// Only send ACKs if we're congestion limited.
|
||||
if !h.congestion.CanSend(h.bytesInFlight) {
|
||||
@@ -529,17 +565,9 @@ func (h *sentPacketHandler) ShouldSendNumPackets() int {
|
||||
return int(math.Ceil(float64(protocol.MinPacingDelay) / float64(delay)))
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) QueueProbePacket() bool {
|
||||
var p *Packet
|
||||
if h.initialPackets != nil {
|
||||
p = h.initialPackets.history.FirstOutstanding()
|
||||
}
|
||||
if p == nil && h.handshakePackets != nil {
|
||||
p = h.handshakePackets.history.FirstOutstanding()
|
||||
}
|
||||
if p == nil {
|
||||
p = h.oneRTTPackets.history.FirstOutstanding()
|
||||
}
|
||||
func (h *sentPacketHandler) QueueProbePacket(encLevel protocol.EncryptionLevel) bool {
|
||||
pnSpace := h.getPacketNumberSpace(encLevel)
|
||||
p := pnSpace.history.FirstOutstanding()
|
||||
if p == nil {
|
||||
return false
|
||||
}
|
||||
@@ -549,8 +577,8 @@ func (h *sentPacketHandler) QueueProbePacket() bool {
|
||||
if p.includedInBytesInFlight {
|
||||
h.bytesInFlight -= p.Length
|
||||
}
|
||||
if err := h.getPacketNumberSpace(p.EncryptionLevel).history.Remove(p.PacketNumber); err != nil {
|
||||
// should never happen. We just got this packet from the history a lines above.
|
||||
if err := pnSpace.history.Remove(p.PacketNumber); err != nil {
|
||||
// should never happen. We just got this packet from the history.
|
||||
panic(err)
|
||||
}
|
||||
return true
|
||||
@@ -573,6 +601,13 @@ func (h *sentPacketHandler) ResetForRetry() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) SetHandshakeComplete() {
|
||||
h.handshakeComplete = true
|
||||
// We don't send PTOs for application data packets before the handshake completes.
|
||||
// Make sure the timer is armed now, if necessary.
|
||||
h.setLossDetectionTimer()
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) GetStats() *quictrace.TransportState {
|
||||
return &quictrace.TransportState{
|
||||
MinRTT: h.rttStats.MinRTT(),
|
||||
|
||||
Reference in New Issue
Block a user