-
Notifications
You must be signed in to change notification settings - Fork 1
/
stopwatch.go
144 lines (126 loc) · 4.25 KB
/
stopwatch.go
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
// Copyright 2019 The Cockroach Authors.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.
package timeutil
import (
"time"
"github.com/cockroachdb/cockroachdb-parser/pkg/util/grunning"
"github.com/cockroachdb/cockroachdb-parser/pkg/util/syncutil"
)
// StopWatch is a utility stop watch that can be safely started and stopped
// multiple times and can be used concurrently.
type StopWatch struct {
mu struct {
syncutil.Mutex
// started is true if the stop watch has been started and haven't been
// stopped after that.
started bool
// startedAt is the time when the stop watch was started.
startedAt time.Time
// elapsed is the total time measured by the stop watch (i.e. between
// all Starts and Stops).
elapsed time.Duration
// timeSource is the source of time used by the stop watch. It is always
// timeutil.Now except for tests.
timeSource func() time.Time
// cpuStopWatch is used to track CPU usage. It may be nil, in which case any
// operations on it are no-ops.
cpuStopWatch *cpuStopWatch
}
}
// NewStopWatch creates a new StopWatch.
func NewStopWatch() *StopWatch {
return newStopWatch(Now)
}
// NewStopWatchWithCPU creates a new StopWatch that will track CPU usage in
// addition to wall-clock time.
func NewStopWatchWithCPU() *StopWatch {
w := newStopWatch(Now)
if grunning.Supported() {
w.mu.cpuStopWatch = &cpuStopWatch{}
}
return w
}
// NewTestStopWatch create a new StopWatch with the given time source. It is
// used for testing only.
func NewTestStopWatch(timeSource func() time.Time) *StopWatch {
return newStopWatch(timeSource)
}
func newStopWatch(timeSource func() time.Time) *StopWatch {
w := &StopWatch{}
w.mu.timeSource = timeSource
return w
}
// Start starts the stop watch if it hasn't already been started.
func (w *StopWatch) Start() {
w.mu.Lock()
defer w.mu.Unlock()
if !w.mu.started {
w.mu.started = true
w.mu.startedAt = w.mu.timeSource()
w.mu.cpuStopWatch.start()
}
}
// Stop stops the stop watch if it hasn't already been stopped and accumulates
// the duration that elapsed since it was started. If the stop watch has
// already been stopped, it is a noop.
func (w *StopWatch) Stop() {
w.mu.Lock()
defer w.mu.Unlock()
if w.mu.started {
w.mu.started = false
w.mu.elapsed += w.mu.timeSource().Sub(w.mu.startedAt)
w.mu.cpuStopWatch.stop()
}
}
// Elapsed returns the total time measured by the stop watch so far.
func (w *StopWatch) Elapsed() time.Duration {
w.mu.Lock()
defer w.mu.Unlock()
return w.mu.elapsed
}
// ElapsedCPU returns the total CPU time measured by the stop watch so far. It
// returns zero if cpuStopWatch is nil (which is the case if NewStopWatchWithCPU
// was not called or the platform does not support grunning).
func (w *StopWatch) ElapsedCPU() time.Duration {
w.mu.Lock()
defer w.mu.Unlock()
return w.mu.cpuStopWatch.elapsed()
}
// LastStartedAt returns the time the stopwatch was last started, and a bool
// indicating if the stopwatch is currently started.
func (w *StopWatch) LastStartedAt() (startedAt time.Time, started bool) {
w.mu.Lock()
defer w.mu.Unlock()
return w.mu.startedAt, w.mu.started
}
// TestTimeSource is a source of time that remembers when it was created (in
// terms of the real time) and returns the time based on its creation time and
// the number of "advances" it has had. It is used for testing only.
type TestTimeSource struct {
initTime time.Time
counter int64
}
// NewTestTimeSource create a new TestTimeSource.
func NewTestTimeSource() *TestTimeSource {
return &TestTimeSource{initTime: Now()}
}
// Now tells the current time according to t.
func (t *TestTimeSource) Now() time.Time {
return t.initTime.Add(time.Duration(t.counter))
}
// Advance advances the current time according to t by 1 nanosecond.
func (t *TestTimeSource) Advance() {
t.counter++
}
// Elapsed returns how much time has passed since t has been created. Note that
// it is equal to the number of advances in nanoseconds.
func (t *TestTimeSource) Elapsed() time.Duration {
return time.Duration(t.counter)
}