package ackhandler
import (
"fmt"
"time"
"github.com/tmc/go-iroh/internal/qng/internal/monotime"
"github.com/tmc/go-iroh/internal/qng/internal/protocol"
"github.com/tmc/go-iroh/internal/qng/internal/utils"
"github.com/tmc/go-iroh/internal/qng/internal/wire"
)
const reorderingThreshold = 1
type receivedPacketTracker struct {
ect0, ect1, ecnce uint64
packetHistory receivedPacketHistory
lastAck *wire .AckFrame
hasNewAck bool
}
func newReceivedPacketTracker() *receivedPacketTracker {
return &receivedPacketTracker {packetHistory : *newReceivedPacketHistory ()}
}
func (h *receivedPacketTracker ) ReceivedPacket (pn protocol .PacketNumber , ecn protocol .ECN , ackEliciting bool ) error {
if isNew := h .packetHistory .ReceivedPacket (pn ); !isNew {
return fmt .Errorf ("receivedPacketTracker BUG: ReceivedPacket called for old / duplicate packet %d" , pn )
}
switch ecn {
case protocol .ECT0 :
h .ect0 ++
case protocol .ECT1 :
h .ect1 ++
case protocol .ECNCE :
h .ecnce ++
}
if !ackEliciting {
return nil
}
h .hasNewAck = true
return nil
}
func (h *receivedPacketTracker ) GetAckFrame () *wire .AckFrame {
if !h .hasNewAck {
return nil
}
ack := h .lastAck
if ack == nil {
ack = &wire .AckFrame {}
}
ack .Reset ()
ack .ECT0 = h .ect0
ack .ECT1 = h .ect1
ack .ECNCE = h .ecnce
for r := range h .packetHistory .Backward () {
ack .AckRanges = append (ack .AckRanges , wire .AckRange {Smallest : r .Start , Largest : r .End })
}
h .lastAck = ack
h .hasNewAck = false
return ack
}
func (h *receivedPacketTracker ) IsPotentiallyDuplicate (pn protocol .PacketNumber ) bool {
return h .packetHistory .IsPotentiallyDuplicate (pn )
}
const packetsBeforeAck = 10
type appDataReceivedPacketTracker struct {
receivedPacketTracker
largestObservedRcvdTime monotime .Time
largestObserved protocol .PacketNumber
ignoreBelow protocol .PacketNumber
maxAckDelay time .Duration
ackQueued bool
ackElicitingPacketsReceivedSinceLastAck int
ackAlarm monotime .Time
logger utils .Logger
}
func newAppDataReceivedPacketTracker(logger utils .Logger ) *appDataReceivedPacketTracker {
h := &appDataReceivedPacketTracker {
receivedPacketTracker : *newReceivedPacketTracker (),
maxAckDelay : protocol .MaxAckDelay ,
logger : logger ,
}
return h
}
func (h *appDataReceivedPacketTracker ) ReceivedPacket (pn protocol .PacketNumber , ecn protocol .ECN , rcvTime monotime .Time , ackEliciting bool ) error {
if err := h .receivedPacketTracker .ReceivedPacket (pn , ecn , ackEliciting ); err != nil {
return err
}
if pn >= h .largestObserved {
h .largestObserved = pn
h .largestObservedRcvdTime = rcvTime
}
if !ackEliciting {
return nil
}
h .ackElicitingPacketsReceivedSinceLastAck ++
isMissing := h .isMissing (pn )
if !h .ackQueued && h .shouldQueueACK (pn , ecn , isMissing ) {
h .ackQueued = true
h .ackAlarm = 0
}
if !h .ackQueued {
h .ackAlarm = rcvTime .Add (h .maxAckDelay )
if h .logger .Debug () {
h .logger .Debugf ("\tSetting ACK timer to max ack delay: %s" , h .maxAckDelay )
}
}
return nil
}
func (h *appDataReceivedPacketTracker ) IgnoreBelow (pn protocol .PacketNumber ) {
if pn <= h .ignoreBelow {
return
}
h .ignoreBelow = pn
h .packetHistory .DeleteBelow (pn )
if h .logger .Debug () {
h .logger .Debugf ("\tIgnoring all packets below %d." , pn )
}
}
func (h *appDataReceivedPacketTracker ) isMissing (p protocol .PacketNumber ) bool {
if h .lastAck == nil || p < h .ignoreBelow {
return false
}
return p < h .lastAck .LargestAcked () && !h .lastAck .AcksPacket (p )
}
func (h *appDataReceivedPacketTracker ) hasNewMissingPackets () bool {
if h .lastAck == nil {
return false
}
if h .largestObserved < reorderingThreshold {
return false
}
highestMissing := h .packetHistory .HighestMissingUpTo (h .largestObserved - reorderingThreshold )
if highestMissing == protocol .InvalidPacketNumber {
return false
}
if highestMissing < h .lastAck .LargestAcked () {
return false
}
return highestMissing > h .lastAck .LargestAcked ()-reorderingThreshold
}
func (h *appDataReceivedPacketTracker ) shouldQueueACK (pn protocol .PacketNumber , ecn protocol .ECN , wasMissing bool ) bool {
if wasMissing {
if h .logger .Debug () {
h .logger .Debugf ("\tQueueing ACK because packet %d was missing before." , pn )
}
return true
}
if h .ackElicitingPacketsReceivedSinceLastAck >= packetsBeforeAck {
if h .logger .Debug () {
h .logger .Debugf ("\tQueueing ACK because packet %d packets were received after the last ACK (using initial threshold: %d)." , h .ackElicitingPacketsReceivedSinceLastAck , packetsBeforeAck )
}
return true
}
if h .hasNewMissingPackets () {
h .logger .Debugf ("\tQueuing ACK because there's a new missing packet to report." )
return true
}
if ecn == protocol .ECNCE {
h .logger .Debugf ("\tQueuing ACK because the packet was ECN-CE marked." )
return true
}
return false
}
func (h *appDataReceivedPacketTracker ) GetAckFrame (now monotime .Time , onlyIfQueued bool ) *wire .AckFrame {
if onlyIfQueued && !h .ackQueued {
if h .ackAlarm .IsZero () || h .ackAlarm .After (now ) {
return nil
}
if h .logger .Debug () && !h .ackAlarm .IsZero () {
h .logger .Debugf ("Sending ACK because the ACK timer expired." )
}
}
ack := h .receivedPacketTracker .GetAckFrame ()
if ack == nil {
return nil
}
ack .DelayTime = max (0 , now .Sub (h .largestObservedRcvdTime ))
h .ackQueued = false
h .ackAlarm = 0
h .ackElicitingPacketsReceivedSinceLastAck = 0
return ack
}
func (h *appDataReceivedPacketTracker ) GetAlarmTimeout () monotime .Time { return h .ackAlarm }
The pages are generated with Golds v0.8.4 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .