Skip to content

Commit

Permalink
Modified FakeTime to allow advances in time.
Browse files Browse the repository at this point in the history
  • Loading branch information
tylerferrara committed Jul 28, 2021
1 parent 1cb2002 commit 96bdd08
Showing 1 changed file with 73 additions and 4 deletions.
77 changes: 73 additions & 4 deletions legacy/timewrapper/timewrapper.go
Expand Up @@ -35,13 +35,82 @@ func (RealTime) Now() time.Time { return time.Now() }
// Sleep simply calls time.Sleep(d), using the given duration.
func (RealTime) Sleep(d time.Duration) { time.Sleep(d) }

// FakeTime holds the global fake time.
// FakeTime is an advancing clock used to mock time.
type FakeTime struct {
Time time.Time
Time time.Time
Interval time.Duration
CheckTime chan bool
Updated chan bool
}

// NewFakeTime creates a new fake time with the provided start time.
func NewFakeTime(startTime time.Time) FakeTime {
return FakeTime{
Time: startTime,
CheckTime: make(chan bool),
Updated: make(chan bool),
}
}

// Advance adds the duration to the global fake time by braking the
// duration up into a series of time-steps. Each time-step is either less than
// or equal to an observed sleep interval. This allows multiple sleep/wake cycles
// to occur on time advancements that are orders longer than the given sleep interval.
func (ft *FakeTime) Advance(d time.Duration) {
// Determine the number of time steps we must make.
var timeStep, timePast time.Duration
timePast = 0
for timePast < d {
timeStep = ft.Interval
if timePast+timeStep > d {
timeStep = d - timePast
}
// Make the time step.
ft.step(timeStep)
timePast += timeStep
}

}

// step increments time by the given duration. This duration should either be less than
// or equal to the observed sleep interval.
func (ft *FakeTime) step(d time.Duration) {
if d > ft.Interval {
panic("timewrapper.step received a duration greater than the observed time interval.")
}
ft.Time = ft.Time.Add(d)
// Notify the sleeping goroutine to check the current time.
ft.CheckTime <- true
// Wait for the sleeping goroutine to respond.
ft.WaitForSleeper()
}

// Now returns the global fake time.
func (ft FakeTime) Now() time.Time { return ft.Time }
func (ft *FakeTime) Now() time.Time { return ft.Time }

// WaitForSleeper blocks until a goroutine has notified that it's up to date
// with the current global fake time.
func (ft *FakeTime) WaitForSleeper() {
<-ft.Updated
}

// Sleep does not block the current goroutine.
func (ft FakeTime) Sleep(d time.Duration) {}
func (ft *FakeTime) Sleep(d time.Duration) {
// Record the sleep interval for the Advance function to break up
// time advancement into a series of time-steps.
ft.Interval = d
// Determine when to wake up from sleep.
wakeAt := ft.Time.Add(d)
// Notify the goroutine is sleeping.
ft.Updated <- true
for {
// Blocking the current goroutine.
<-ft.CheckTime
if wakeAt.After(ft.Time) {
// Notify the goroutine has seen the fake time update.
ft.Updated <- true
} else {
return
}
}
}

0 comments on commit 96bdd08

Please sign in to comment.