forked from quic-go/quic-go
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
use a monotonous timer for the connection (quic-go#3570)
There's no point in having the timer fire multiple times for the same timestamp. By using a monotonuos timer we avoid busy-looping in cases where the timer fires, but we can't actually send a packet.
- Loading branch information
1 parent
bf29e68
commit 63f9112
Showing
5 changed files
with
137 additions
and
18 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
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,51 @@ | ||
package quic | ||
|
||
import ( | ||
"time" | ||
|
||
"github.com/lucas-clemente/quic-go/internal/utils" | ||
) | ||
|
||
var deadlineSendImmediately = time.Time{}.Add(42 * time.Millisecond) // any value > time.Time{} and before time.Now() is fine | ||
|
||
type connectionTimer struct { | ||
timer *utils.Timer | ||
last time.Time | ||
} | ||
|
||
func newTimer() *connectionTimer { | ||
return &connectionTimer{timer: utils.NewTimer()} | ||
} | ||
|
||
func (t *connectionTimer) SetRead() { | ||
if deadline := t.timer.Deadline(); deadline != deadlineSendImmediately { | ||
t.last = deadline | ||
} | ||
t.timer.SetRead() | ||
} | ||
|
||
func (t *connectionTimer) Chan() <-chan time.Time { | ||
return t.timer.Chan() | ||
} | ||
|
||
// SetTimer resets the timer. | ||
// It makes sure that the deadline is strictly increasing. | ||
// This prevents busy-looping in cases where the timer fires, but we can't actually send out a packet. | ||
// This doesn't apply to the pacing deadline, which can be set multiple times to deadlineSendImmediately. | ||
func (t *connectionTimer) SetTimer(idleTimeoutOrKeepAlive, ackAlarm, lossTime, pacing time.Time) { | ||
deadline := idleTimeoutOrKeepAlive | ||
if !ackAlarm.IsZero() && ackAlarm.Before(deadline) && ackAlarm.After(t.last) { | ||
deadline = ackAlarm | ||
} | ||
if !lossTime.IsZero() && lossTime.Before(deadline) && lossTime.After(t.last) { | ||
deadline = lossTime | ||
} | ||
if !pacing.IsZero() && pacing.Before(deadline) { | ||
deadline = pacing | ||
} | ||
t.timer.Reset(deadline) | ||
} | ||
|
||
func (t *connectionTimer) Stop() { | ||
t.timer.Stop() | ||
} |
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,62 @@ | ||
package quic | ||
|
||
import ( | ||
"time" | ||
|
||
. "github.com/onsi/ginkgo" | ||
. "github.com/onsi/gomega" | ||
) | ||
|
||
func (t *connectionTimer) Deadline() time.Time { return t.timer.Deadline() } | ||
|
||
var _ = Describe("Timer", func() { | ||
It("sets an idle timeout", func() { | ||
now := time.Now() | ||
t := newTimer() | ||
t.SetTimer(now.Add(time.Hour), time.Time{}, time.Time{}, time.Time{}) | ||
Expect(t.Deadline()).To(Equal(now.Add(time.Hour))) | ||
}) | ||
|
||
It("sets an ACK timer", func() { | ||
now := time.Now() | ||
t := newTimer() | ||
t.SetTimer(now.Add(time.Hour), now.Add(time.Minute), time.Time{}, time.Time{}) | ||
Expect(t.Deadline()).To(Equal(now.Add(time.Minute))) | ||
}) | ||
|
||
It("sets a loss timer", func() { | ||
now := time.Now() | ||
t := newTimer() | ||
t.SetTimer(now.Add(time.Hour), now.Add(time.Minute), now.Add(time.Second), time.Time{}) | ||
Expect(t.Deadline()).To(Equal(now.Add(time.Second))) | ||
}) | ||
|
||
It("sets a pacing timer", func() { | ||
now := time.Now() | ||
t := newTimer() | ||
t.SetTimer(now.Add(time.Hour), now.Add(time.Minute), now.Add(time.Second), now.Add(time.Millisecond)) | ||
Expect(t.Deadline()).To(Equal(now.Add(time.Millisecond))) | ||
}) | ||
|
||
It("doesn't reset to an earlier time", func() { | ||
now := time.Now() | ||
t := newTimer() | ||
t.SetTimer(now.Add(time.Hour), now.Add(time.Minute), time.Time{}, time.Time{}) | ||
Expect(t.Deadline()).To(Equal(now.Add(time.Minute))) | ||
t.SetRead() | ||
|
||
t.SetTimer(now.Add(time.Hour), now.Add(time.Minute), time.Time{}, time.Time{}) | ||
Expect(t.Deadline()).To(Equal(now.Add(time.Hour))) | ||
}) | ||
|
||
It("allows the pacing timer to be set to send immediately", func() { | ||
now := time.Now() | ||
t := newTimer() | ||
t.SetTimer(now.Add(time.Hour), now.Add(time.Minute), time.Time{}, time.Time{}) | ||
Expect(t.Deadline()).To(Equal(now.Add(time.Minute))) | ||
t.SetRead() | ||
|
||
t.SetTimer(now.Add(time.Hour), now.Add(time.Minute), time.Time{}, deadlineSendImmediately) | ||
Expect(t.Deadline()).To(Equal(deadlineSendImmediately)) | ||
}) | ||
}) |
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
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