File: clock.go

package info (click to toggle)
golang-github-jmhodges-clock 1.0-3~bpo8%2B1
  • links: PTS, VCS
  • area: main
  • in suites: jessie-backports
  • size: 92 kB
  • sloc: makefile: 2
file content (214 lines) | stat: -rw-r--r-- 5,656 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
// Package clock provides an abstraction for system time that enables
// testing of time-sensitive code.
//
// Where you'd use time.Now, instead use clk.Now where clk is an
// instance of Clock.
//
// When running your code in production, pass it a Clock given by
// Default() and when you're running it in your tests, pass it an
// instance of Clock from NewFake().
//
// When you do that, you can use FakeClock's Add and Set methods to
// control how time behaves in your code making them more reliable
// while also expanding the space of problems you can test.
//
// This code intentionally does not attempt to provide an abstraction
// over time.Ticker and time.Timer because Go does not have the
// runtime or API hooks available to do reliably. See
// https://github.com/golang/go/issues/8869
//
// Be sure to test Time equality with time.Time#Equal, not ==.
package clock

import (
	"sort"
	"sync"
	"time"
)

var systemClock Clock = sysClock{}

// Default returns a Clock that matches the actual system time.
func Default() Clock {
	// This is a method instead of a public var to prevent folks from
	// "making things work" by writing to the var instead of passing
	// in a Clock.
	return systemClock
}

// Clock is an abstraction over system time. New instances of it can
// be made with Default and NewFake.
type Clock interface {
	// Now returns the Clock's current view of the time. Mutating the
	// returned Time will not mutate the clock's time.
	Now() time.Time

	// Sleep causes the current goroutine to sleep for the given duration.
	Sleep(time.Duration)

	// After returns a channel that fires after the given duration.
	After(time.Duration) <-chan time.Time

	// NewTimer makes a Timer based on this clock's time. Using Timers and
	// negative durations in the Clock or Timer API is undefined behavior and
	// may be changed.
	NewTimer(time.Duration) *Timer
}

type sysClock struct{}

func (s sysClock) Now() time.Time {
	return time.Now()
}

func (s sysClock) Sleep(d time.Duration) {
	time.Sleep(d)
}

func (s sysClock) After(d time.Duration) <-chan time.Time {
	return time.After(d)
}

func (s sysClock) NewTimer(d time.Duration) *Timer {
	tt := time.NewTimer(d)
	return &Timer{C: tt.C, timer: tt}
}

// NewFake returns a FakeClock to be used in tests that need to
// manipulate time. Its initial value is always the unix epoch in the
// UTC timezone. The FakeClock returned is thread-safe.
func NewFake() FakeClock {
	// We're explicit about this time construction to avoid early user
	// questions about why the time object doesn't have a Location by
	// default.
	return &fake{t: time.Unix(0, 0).UTC()}
}

// FakeClock is a Clock with additional controls. The return value of
// Now return can be modified with Add. Use NewFake to get a
// thread-safe FakeClock implementation.
type FakeClock interface {
	Clock
	// Adjust the time that will be returned by Now.
	Add(d time.Duration)

	// Set the Clock's time to exactly the time given.
	Set(t time.Time)
}

// To prevent mistakes with the API, we hide this behind NewFake. It's
// easy forget to create a pointer to a fake since time.Time (and
// sync.Mutex) are also simple values. The code will appear to work
// but the clock's time will never be adjusted.
type fake struct {
	sync.RWMutex
	t     time.Time
	sends sortedSends
}

func (f *fake) Now() time.Time {
	f.RLock()
	defer f.RUnlock()
	return f.t
}

func (f *fake) Sleep(d time.Duration) {
	if d < 0 {
		// time.Sleep just returns immediately. Do the same.
		return
	}
	f.Add(d)
}

func (f *fake) After(d time.Duration) <-chan time.Time {
	return f.NewTimer(d).C
}

func (f *fake) NewTimer(d time.Duration) *Timer {
	f.Lock()
	defer f.Unlock()
	ch := make(chan time.Time, 1)
	tt := f.t.Add(d)
	ft := &fakeTimer{c: ch, clk: f, active: true}
	t := &Timer{
		C:         ch,
		fakeTimer: ft,
	}
	s := f.addSend(tt, ft)
	ft.sends = []*send{s}
	return t
}

func (f *fake) Add(d time.Duration) {
	f.Lock()
	defer f.Unlock()
	f.t = f.t.Add(d)
	f.sendTimes()
}

func (f *fake) Set(t time.Time) {
	f.Lock()
	defer f.Unlock()
	f.t = t
	f.sendTimes()
}

// Only to be called while the fake's lock is held
func (f *fake) sendTimes() {
	newSends := make(sortedSends, 0)
	for _, s := range f.sends {
		if !s.active || !s.ft.active {
			continue
		}
		if s.target.Equal(f.t) || s.target.Before(f.t) {
			s.ft.active = false
			s.active = false
			// The select is to drop second sends from resets without a user
			// receiving from ft.c.
			select {
			case s.ft.c <- s.target:
			default:
			}
		}
		if s.active {
			newSends = append(newSends, s)
		}
	}
	f.sends = newSends
}

// Only to be called while the fake's lock is held
func (f *fake) addSend(target time.Time, ft *fakeTimer) *send {
	s := &send{target: target, ft: ft, active: true}
	f.sends = append(f.sends, s)
	// This will be a small enough slice to be fast. Can be replaced with a more
	// complicated container if someone is making many timers.
	sort.Sort(f.sends)
	return s
}

// send is a struct that represents a scheduled send of a time.Time to its
// fakeTimer's channel. They are actually sent when the relevant fake's time
// goes equal or past their target time, as long as the relevant fakeTimer has
// not been Reset or Stop'ed. When a Timer is Reset, the old sends are
// deactivated and will be removed from the clocks list on the next attempt to
// send.
type send struct {
	target time.Time
	active bool
	ft     *fakeTimer
}

type sortedSends []*send

func (s sortedSends) Len() int {
	return len(s)
}

func (s sortedSends) Less(i, j int) bool {
	return s[i].target.Before(s[j].target)
}

func (s sortedSends) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}