blob: d257a85523dcd63ed1b7525f2a7fb9c4c2395fd3 [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package vif
import (
"sync"
"time"
)
// Since an idle timer with a short timeout can expire before establishing a VC,
// we provide a fake timer to reduce dependence on real time in unittests.
type fakeTimer struct {
mu sync.Mutex
timeout time.Duration
timeoutFunc func()
timer timer
stopped bool
}
func newFakeTimer(d time.Duration, f func()) *fakeTimer {
return &fakeTimer{
timeout: d,
timeoutFunc: f,
timer: noopTimer{},
}
}
func (t *fakeTimer) Stop() bool {
t.mu.Lock()
defer t.mu.Unlock()
t.stopped = true
return t.timer.Stop()
}
func (t *fakeTimer) Reset(d time.Duration) bool {
t.mu.Lock()
defer t.mu.Unlock()
active := t.timer.Stop()
t.timeout = d
t.stopped = false
if t.timeout > 0 {
t.timer = newTimer(t.timeout, t.timeoutFunc)
} else {
t.timer = noopTimer{}
}
return active
}
func (t *fakeTimer) run(release <-chan struct{}, wg *sync.WaitGroup) {
defer wg.Done()
<-release // Wait until notified to run.
t.mu.Lock()
defer t.mu.Unlock()
if t.timeout > 0 && !t.stopped {
t.timer = newTimer(t.timeout, t.timeoutFunc)
}
}
// SetFakeTimers causes the idle timers to use a fake timer instead of one
// based on real time. The timers will be triggered when the returned function
// is invoked. (at which point the timer setup will be restored to what it was
// before calling this function)
//
// Usage:
// triggerTimers := SetFakeTimers()
// ...
// triggerTimers()
//
// This function cannot be called concurrently.
func SetFakeTimers() func() {
backup := newTimer
var mu sync.Mutex
var wg sync.WaitGroup
release := make(chan struct{})
newTimer = func(d time.Duration, f func()) timer {
mu.Lock()
defer mu.Unlock()
wg.Add(1)
t := newFakeTimer(d, f)
go t.run(release, &wg)
return t
}
return func() {
mu.Lock()
defer mu.Unlock()
newTimer = backup
close(release)
wg.Wait()
}
}