Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(143)

Side by Side Diff: go/src/infra/libs/clock/testclock/testclock.go

Issue 1154213012: Add context-aware "time" library wrapper. (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@master
Patch Set: Removed goroutine safety from testtimer. Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698