github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/util/timeutil/stopwatch.go (about) 1 // Copyright 2019 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package timeutil 12 13 import ( 14 "sync/atomic" 15 "time" 16 ) 17 18 // StopWatch is a utility stop watch that can be safely started and stopped 19 // multiple times and can be used concurrently. 20 type StopWatch struct { 21 // started is used as boolean where 0 indicates false and 1 indicates true. 22 // started is "true" if the stop watch has been started. 23 started int32 24 // startedAt is the time when the stop watch was started. 25 startedAt time.Time 26 // elapsed is the total time measured by the stop watch (i.e. between all 27 // Starts and Stops). 28 elapsed time.Duration 29 // timeSource is the source of time used by the stop watch. It is always 30 // timeutil.Now except for tests. 31 timeSource func() time.Time 32 } 33 34 // NewStopWatch creates a new StopWatch. 35 func NewStopWatch() *StopWatch { 36 return newStopWatch(Now) 37 } 38 39 // NewTestStopWatch create a new StopWatch with the given time source. It is 40 // used for testing only. 41 func NewTestStopWatch(timeSource func() time.Time) *StopWatch { 42 return newStopWatch(timeSource) 43 } 44 45 func newStopWatch(timeSource func() time.Time) *StopWatch { 46 return &StopWatch{timeSource: timeSource} 47 } 48 49 // Start starts the stop watch if it hasn't already been started. 50 func (w *StopWatch) Start() { 51 if atomic.CompareAndSwapInt32(&w.started, 0, 1) { 52 w.startedAt = w.timeSource() 53 } 54 } 55 56 // Stop stops the stop watch if it hasn't already been stopped and accumulates 57 // the duration that elapsed since it was started. If the stop watch has 58 // already been stopped, it is a noop. 59 func (w *StopWatch) Stop() { 60 if atomic.CompareAndSwapInt32(&w.started, 1, 0) { 61 w.elapsed += w.timeSource().Sub(w.startedAt) 62 } 63 } 64 65 // Elapsed returns the total time measured by the stop watch so far. 66 func (w *StopWatch) Elapsed() time.Duration { 67 return w.elapsed 68 } 69 70 // TestTimeSource is a source of time that remembers when it was created (in 71 // terms of the real time) and returns the time based on its creation time and 72 // the number of "advances" it has had. It is used for testing only. 73 type TestTimeSource struct { 74 initTime time.Time 75 counter int64 76 } 77 78 // NewTestTimeSource create a new TestTimeSource. 79 func NewTestTimeSource() *TestTimeSource { 80 return &TestTimeSource{initTime: Now()} 81 } 82 83 // Now tells the current time according to t. 84 func (t *TestTimeSource) Now() time.Time { 85 return t.initTime.Add(time.Duration(t.counter)) 86 } 87 88 // Advance advances the current time according to t by 1 nanosecond. 89 func (t *TestTimeSource) Advance() { 90 t.counter++ 91 } 92 93 // Elapsed returns how much time has passed since t has been created. Note that 94 // it is equal to the number of advances in nanoseconds. 95 func (t *TestTimeSource) Elapsed() time.Duration { 96 return time.Duration(t.counter) 97 }