diff --git a/internal/congestion/pacer.go b/internal/congestion/pacer.go index b78ec1f2..1a8b37cb 100644 --- a/internal/congestion/pacer.go +++ b/internal/congestion/pacer.go @@ -18,10 +18,9 @@ type pacer struct { } func newPacer(bw uint64) *pacer { - return &pacer{ - bandwidth: bw, - budgetAtLastSent: maxBurstSize, - } + p := &pacer{bandwidth: bw} + p.budgetAtLastSent = p.maxBurstSize() + return p } func (p *pacer) SentPacket(sendTime time.Time, size protocol.ByteCount) { @@ -43,11 +42,16 @@ func (p *pacer) SetBandwidth(bw uint64) { func (p *pacer) Budget(now time.Time) protocol.ByteCount { if p.lastSentTime.IsZero() { - return p.budgetAtLastSent + return p.maxBurstSize() } - return utils.MinByteCount( + budget := p.budgetAtLastSent + (protocol.ByteCount(p.bandwidth)*protocol.ByteCount(now.Sub(p.lastSentTime).Nanoseconds()))/1e9 + return utils.MinByteCount(p.maxBurstSize(), budget) +} + +func (p *pacer) maxBurstSize() protocol.ByteCount { + return utils.MaxByteCount( + protocol.ByteCount(uint64((protocol.MinPacingDelay+protocol.TimerGranularity).Nanoseconds())*p.bandwidth)/1e9, maxBurstSize, - p.budgetAtLastSent+(protocol.ByteCount(p.bandwidth)*protocol.ByteCount(now.Sub(p.lastSentTime).Nanoseconds()))/1e9, ) } @@ -56,6 +60,8 @@ func (p *pacer) TimeUntilSend() time.Time { if p.budgetAtLastSent >= maxDatagramSize { return time.Time{} } - // TODO: don't allow pacing faster than MinPacingDelay - return p.lastSentTime.Add(time.Duration(math.Ceil(float64(maxDatagramSize-p.budgetAtLastSent)*1e9/float64(p.bandwidth))) * time.Nanosecond) + return p.lastSentTime.Add(utils.MaxDuration( + protocol.MinPacingDelay, + time.Duration(math.Ceil(float64(maxDatagramSize-p.budgetAtLastSent)*1e9/float64(p.bandwidth)))*time.Nanosecond, + )) } diff --git a/internal/congestion/pacer_test.go b/internal/congestion/pacer_test.go index 937213f3..b366cff5 100644 --- a/internal/congestion/pacer_test.go +++ b/internal/congestion/pacer_test.go @@ -3,6 +3,8 @@ package congestion import ( "time" + "github.com/lucas-clemente/quic-go/internal/protocol" + . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) @@ -22,6 +24,13 @@ var _ = Describe("Pacer", func() { Expect(p.Budget(t)).To(BeEquivalentTo(maxBurstSize)) }) + It("allows a big burst for high pacing rates", func() { + t := time.Now() + p.SetBandwidth(10000 * packetsPerSecond * uint64(maxDatagramSize)) + Expect(p.TimeUntilSend()).To(BeZero()) + Expect(p.Budget(t)).To(BeNumerically(">", maxBurstSize)) + }) + It("reduces the budget when sending packets", func() { t := time.Now() budget := p.Budget(t) @@ -88,4 +97,12 @@ var _ = Describe("Pacer", func() { p.SetBandwidth(uint64(maxDatagramSize)) // reduce the bandwidth to 1 packet per second Expect(p.TimeUntilSend()).To(Equal(t.Add(time.Second))) }) + + It("doesn't pace faster than the minimum pacing duration", func() { + t := time.Now() + sendBurst(t) + p.SetBandwidth(1e6 * uint64(maxDatagramSize)) + Expect(p.TimeUntilSend()).To(Equal(t.Add(protocol.MinPacingDelay))) + Expect(p.Budget(t.Add(protocol.MinPacingDelay))).To(Equal(protocol.ByteCount(protocol.MinPacingDelay) * maxDatagramSize * 1e6 / 1e9)) + }) })