mirror of
https://github.com/cloudflare/cloudflared.git
synced 2025-07-27 14:49:57 +00:00
TUN-528: Move cloudflared into a separate repo
This commit is contained in:
81
h2mux/idletimer.go
Normal file
81
h2mux/idletimer.go
Normal file
@@ -0,0 +1,81 @@
|
||||
package h2mux
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// IdleTimer is a type of Timer designed for managing heartbeats on an idle connection.
|
||||
// The timer ticks on an interval with added jitter to avoid accidental synchronisation
|
||||
// between two endpoints. It tracks the number of retries/ticks since the connection was
|
||||
// last marked active.
|
||||
//
|
||||
// The methods of IdleTimer must not be called while a goroutine is reading from C.
|
||||
type IdleTimer struct {
|
||||
// The channel on which ticks are delivered.
|
||||
C <-chan time.Time
|
||||
|
||||
// A timer used to measure idle connection time. Reset after sending data.
|
||||
idleTimer *time.Timer
|
||||
// The maximum length of time a connection is idle before sending a ping.
|
||||
idleDuration time.Duration
|
||||
// A pseudorandom source used to add jitter to the idle duration.
|
||||
randomSource *rand.Rand
|
||||
// The maximum number of retries allowed.
|
||||
maxRetries uint64
|
||||
// The number of retries since the connection was last marked active.
|
||||
retries uint64
|
||||
// A lock to prevent race condition while checking retries
|
||||
stateLock sync.RWMutex
|
||||
}
|
||||
|
||||
func NewIdleTimer(idleDuration time.Duration, maxRetries uint64) *IdleTimer {
|
||||
t := &IdleTimer{
|
||||
idleTimer: time.NewTimer(idleDuration),
|
||||
idleDuration: idleDuration,
|
||||
randomSource: rand.New(rand.NewSource(time.Now().Unix())),
|
||||
maxRetries: maxRetries,
|
||||
}
|
||||
t.C = t.idleTimer.C
|
||||
return t
|
||||
}
|
||||
|
||||
// Retry should be called when retrying the idle timeout. If the maximum number of retries
|
||||
// has been met, returns false.
|
||||
// After calling this function and sending a heartbeat, call ResetTimer. Since sending the
|
||||
// heartbeat could be a blocking operation, we resetting the timer after the write completes
|
||||
// to avoid it expiring during the write.
|
||||
func (t *IdleTimer) Retry() bool {
|
||||
t.stateLock.Lock()
|
||||
defer t.stateLock.Unlock()
|
||||
if t.retries >= t.maxRetries {
|
||||
return false
|
||||
}
|
||||
t.retries++
|
||||
return true
|
||||
}
|
||||
|
||||
func (t *IdleTimer) RetryCount() uint64 {
|
||||
t.stateLock.RLock()
|
||||
defer t.stateLock.RUnlock()
|
||||
return t.retries
|
||||
}
|
||||
|
||||
// MarkActive resets the idle connection timer and suppresses any outstanding idle events.
|
||||
func (t *IdleTimer) MarkActive() {
|
||||
if !t.idleTimer.Stop() {
|
||||
// eat the timer event to prevent spurious pings
|
||||
<-t.idleTimer.C
|
||||
}
|
||||
t.stateLock.Lock()
|
||||
t.retries = 0
|
||||
t.stateLock.Unlock()
|
||||
t.ResetTimer()
|
||||
}
|
||||
|
||||
// Reset the idle timer according to the configured duration, with some added jitter.
|
||||
func (t *IdleTimer) ResetTimer() {
|
||||
jitter := time.Duration(t.randomSource.Int63n(int64(t.idleDuration)))
|
||||
t.idleTimer.Reset(t.idleDuration + jitter)
|
||||
}
|
Reference in New Issue
Block a user