Chromium Code Reviews| 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 f or | |
| 23 // sychronizing state when testing. | |
|
iannucci
2015/06/03 17:37:20
I think we're still trying to wrap 80 on comments
dnj
2015/06/03 18:21:27
I did a best-effort run. I wonder if this is enfor
| |
| 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 membe r | |
| 31 // variables. Time-based events are explicitly triggered by sending on a Timer | |
| 32 // 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 func (c *testClock) whenTimePasses(threshold time.Time, callback func(time.Time) ) cancelFunc { | |
| 129 stopC := make(chan struct{}) | |
| 130 finishedC := make(chan struct{}) | |
| 131 | |
| 132 // Our control goroutine will monitor both time and stop signals. It wil l only terminate | |
| 133 // when stopC has been closed. | |
| 134 // | |
| 135 // The lock that we take our here is owned by the following goroutine. | |
| 136 c.Lock() | |
| 137 go func() { | |
| 138 defer func() { | |
| 139 now := c.now | |
| 140 c.Unlock() | |
| 141 close(finishedC) | |
| 142 callback(now) | |
| 143 }() | |
| 144 | |
| 145 for { | |
| 146 // Determine if we are past our signalling threshold. We can safely access our | |
| 147 // clock's time member directly, since we hold its lock from the condition firing. | |
| 148 if !c.now.Before(threshold) { | |
| 149 return | |
| 150 } | |
| 151 | |
| 152 // Wait for a signal from our clock's condition. | |
| 153 c.timerCond.Wait() | |
| 154 | |
| 155 // Have we been stopped? | |
| 156 select { | |
| 157 case <-stopC: | |
| 158 return | |
| 159 | |
| 160 default: | |
| 161 // Nope. | |
| 162 } | |
| 163 } | |
| 164 }() | |
| 165 | |
| 166 return func() { | |
| 167 // Close our stop channel and block pending goroutine terminatio n. | |
| 168 close(stopC) | |
| 169 c.pokeTimers(false) | |
| 170 <-finishedC | |
| 171 } | |
| 172 } | |
| OLD | NEW |