github.com/ooni/psiphon/tunnel-core@v0.0.0-20230105123940-fe12a24c96ee/oovendor/quic-go/internal/congestion/pacer.go (about)

     1  package congestion
     2  
     3  import (
     4  	"math"
     5  	"time"
     6  
     7  	"github.com/ooni/psiphon/tunnel-core/oovendor/quic-go/internal/protocol"
     8  	"github.com/ooni/psiphon/tunnel-core/oovendor/quic-go/internal/utils"
     9  )
    10  
    11  const maxBurstSizePackets = 10
    12  
    13  // The pacer implements a token bucket pacing algorithm.
    14  type pacer struct {
    15  	budgetAtLastSent     protocol.ByteCount
    16  	maxDatagramSize      protocol.ByteCount
    17  	lastSentTime         time.Time
    18  	getAdjustedBandwidth func() uint64 // in bytes/s
    19  }
    20  
    21  func newPacer(getBandwidth func() Bandwidth) *pacer {
    22  	p := &pacer{
    23  		maxDatagramSize: initialMaxDatagramSize,
    24  		getAdjustedBandwidth: func() uint64 {
    25  			// Bandwidth is in bits/s. We need the value in bytes/s.
    26  			bw := uint64(getBandwidth() / BytesPerSecond)
    27  			// Use a slightly higher value than the actual measured bandwidth.
    28  			// RTT variations then won't result in under-utilization of the congestion window.
    29  			// Ultimately, this will  result in sending packets as acknowledgments are received rather than when timers fire,
    30  			// provided the congestion window is fully utilized and acknowledgments arrive at regular intervals.
    31  			return bw * 5 / 4
    32  		},
    33  	}
    34  	p.budgetAtLastSent = p.maxBurstSize()
    35  	return p
    36  }
    37  
    38  func (p *pacer) SentPacket(sendTime time.Time, size protocol.ByteCount) {
    39  	budget := p.Budget(sendTime)
    40  	if size > budget {
    41  		p.budgetAtLastSent = 0
    42  	} else {
    43  		p.budgetAtLastSent = budget - size
    44  	}
    45  	p.lastSentTime = sendTime
    46  }
    47  
    48  func (p *pacer) Budget(now time.Time) protocol.ByteCount {
    49  	if p.lastSentTime.IsZero() {
    50  		return p.maxBurstSize()
    51  	}
    52  	budget := p.budgetAtLastSent + (protocol.ByteCount(p.getAdjustedBandwidth())*protocol.ByteCount(now.Sub(p.lastSentTime).Nanoseconds()))/1e9
    53  	return utils.MinByteCount(p.maxBurstSize(), budget)
    54  }
    55  
    56  func (p *pacer) maxBurstSize() protocol.ByteCount {
    57  	return utils.MaxByteCount(
    58  		protocol.ByteCount(uint64((protocol.MinPacingDelay+protocol.TimerGranularity).Nanoseconds())*p.getAdjustedBandwidth())/1e9,
    59  		maxBurstSizePackets*p.maxDatagramSize,
    60  	)
    61  }
    62  
    63  // TimeUntilSend returns when the next packet should be sent.
    64  // It returns the zero value of time.Time if a packet can be sent immediately.
    65  func (p *pacer) TimeUntilSend() time.Time {
    66  	if p.budgetAtLastSent >= p.maxDatagramSize {
    67  		return time.Time{}
    68  	}
    69  	return p.lastSentTime.Add(utils.MaxDuration(
    70  		protocol.MinPacingDelay,
    71  		time.Duration(math.Ceil(float64(p.maxDatagramSize-p.budgetAtLastSent)*1e9/float64(p.getAdjustedBandwidth())))*time.Nanosecond,
    72  	))
    73  }
    74  
    75  func (p *pacer) SetMaxDatagramSize(s protocol.ByteCount) {
    76  	p.maxDatagramSize = s
    77  }