| OLD | NEW |
| (Empty) |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 package testclock | |
| 6 | |
| 7 import ( | |
| 8 "sync" | |
| 9 "time" | |
| 10 | |
| 11 "infra/libs/clock" | |
| 12 ) | |
| 13 | |
| 14 // TestClock is a Clock interface with additional methods to help instrument it. | |
| 15 type TestClock interface { | |
| 16 clock.Clock | |
| 17 Set(time.Time) | |
| 18 Add(time.Duration) | |
| 19 SetTimerCallback(TimerCallback) | |
| 20 } | |
| 21 | |
| 22 // TimerCallback that can be invoked when a timer has been set. This is useful | |
| 23 // for sychronizing state when testing. | |
| 24 type TimerCallback func(clock.Timer) | |
| 25 | |
| 26 type cancelFunc func() | |
| 27 | |
| 28 // testClock is a test-oriented implementation of the 'Clock' interface. | |
| 29 // | |
| 30 // This implementation's Clock responses are configurable by modifying its | |
| 31 // member variables. Time-based events are explicitly triggered by sending on a | |
| 32 // Timer instance's channel. | |
| 33 type testClock struct { | |
| 34 sync.RWMutex | |
| 35 | |
| 36 now time.Time // The current clock time. | |
| 37 timerCond *sync.Cond // Condition used to manage timer blocking. | |
| 38 | |
| 39 timerCallback TimerCallback // Optional callback when a timer has been s
et. | |
| 40 } | |
| 41 | |
| 42 var _ TestClock = (*testClock)(nil) | |
| 43 | |
| 44 // New returns a TestClock instance set at the specified time. | |
| 45 func New(now time.Time) TestClock { | |
| 46 c := testClock{ | |
| 47 now: now, | |
| 48 } | |
| 49 c.timerCond = sync.NewCond(&c) | |
| 50 return &c | |
| 51 } | |
| 52 | |
| 53 func (c *testClock) Now() time.Time { | |
| 54 c.RLock() | |
| 55 defer c.RUnlock() | |
| 56 | |
| 57 return c.now | |
| 58 } | |
| 59 | |
| 60 func (c *testClock) Sleep(d time.Duration) { | |
| 61 <-c.After(d) | |
| 62 } | |
| 63 | |
| 64 func (c *testClock) NewTimer() clock.Timer { | |
| 65 return newTimer(c) | |
| 66 } | |
| 67 | |
| 68 func (c *testClock) After(d time.Duration) <-chan time.Time { | |
| 69 t := c.NewTimer() | |
| 70 t.Reset(d) | |
| 71 return t.GetC() | |
| 72 } | |
| 73 | |
| 74 func (c *testClock) Set(t time.Time) { | |
| 75 c.Lock() | |
| 76 defer c.Unlock() | |
| 77 | |
| 78 c.setTimeLocked(t) | |
| 79 } | |
| 80 | |
| 81 func (c *testClock) Add(d time.Duration) { | |
| 82 c.Lock() | |
| 83 defer c.Unlock() | |
| 84 | |
| 85 c.setTimeLocked(c.now.Add(d)) | |
| 86 } | |
| 87 | |
| 88 func (c *testClock) setTimeLocked(t time.Time) { | |
| 89 if t.Before(c.now) { | |
| 90 panic("Cannot go backwards in time. You're not Doc Brown.") | |
| 91 } | |
| 92 c.now = t | |
| 93 | |
| 94 // Unblock any blocking timers that are waiting on our lock. | |
| 95 c.pokeTimers(true) | |
| 96 } | |
| 97 | |
| 98 func (c *testClock) SetTimerCallback(callback TimerCallback) { | |
| 99 c.Lock() | |
| 100 defer c.Unlock() | |
| 101 | |
| 102 c.timerCallback = callback | |
| 103 } | |
| 104 | |
| 105 func (c *testClock) getTimerCallback() TimerCallback { | |
| 106 c.Lock() | |
| 107 defer c.Unlock() | |
| 108 | |
| 109 return c.timerCallback | |
| 110 } | |
| 111 | |
| 112 func (c *testClock) signalTimerSet(t clock.Timer) { | |
| 113 callback := c.getTimerCallback() | |
| 114 if callback != nil { | |
| 115 callback(t) | |
| 116 } | |
| 117 } | |
| 118 | |
| 119 func (c *testClock) pokeTimers(locked bool) { | |
| 120 if !locked { | |
| 121 c.Lock() | |
| 122 defer c.Unlock() | |
| 123 } | |
| 124 | |
| 125 c.timerCond.Broadcast() | |
| 126 } | |
| 127 | |
| 128 // invokeAt invokes the specified callback when the Clock has advanced at | |
| 129 // or after the specified threshold. | |
| 130 // | |
| 131 // The returned cancelFunc can be used to cancel the blocking. If the cancel | |
| 132 // function is invoked before the callback, the callback will not be invoked. | |
| 133 func (c *testClock) invokeAt(threshold time.Time, callback func(time.Time)) canc
elFunc { | |
| 134 stopC := make(chan struct{}) | |
| 135 finishedC := make(chan struct{}) | |
| 136 | |
| 137 // Our control goroutine will monitor both time and stop signals. It wil
l only | |
| 138 // terminate when stopC has been closed. | |
| 139 // | |
| 140 // The lock that we take our here is owned by the following goroutine. | |
| 141 c.Lock() | |
| 142 go func() { | |
| 143 defer func() { | |
| 144 now := c.now | |
| 145 c.Unlock() | |
| 146 close(finishedC) | |
| 147 callback(now) | |
| 148 }() | |
| 149 | |
| 150 for { | |
| 151 // Determine if we are past our signalling threshold. We
can safely access | |
| 152 // our clock's time member directly, since we hold its l
ock from the | |
| 153 // condition firing. | |
| 154 if !c.now.Before(threshold) { | |
| 155 return | |
| 156 } | |
| 157 | |
| 158 // Wait for a signal from our clock's condition. | |
| 159 c.timerCond.Wait() | |
| 160 | |
| 161 // Have we been stopped? | |
| 162 select { | |
| 163 case <-stopC: | |
| 164 return | |
| 165 | |
| 166 default: | |
| 167 // Nope. | |
| 168 } | |
| 169 } | |
| 170 }() | |
| 171 | |
| 172 return func() { | |
| 173 // Close our stop channel and block pending goroutine terminatio
n. | |
| 174 close(stopC) | |
| 175 c.pokeTimers(false) | |
| 176 <-finishedC | |
| 177 } | |
| 178 } | |
| OLD | NEW |