github.com/cockroachdb/cockroachdb-parser@v0.23.3-0.20240213214944-911057d40c9a/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 "time" 15 16 "github.com/cockroachdb/cockroachdb-parser/pkg/util/grunning" 17 "github.com/cockroachdb/cockroachdb-parser/pkg/util/syncutil" 18 ) 19 20 // StopWatch is a utility stop watch that can be safely started and stopped 21 // multiple times and can be used concurrently. 22 type StopWatch struct { 23 mu struct { 24 syncutil.Mutex 25 // started is true if the stop watch has been started and haven't been 26 // stopped after that. 27 started bool 28 // startedAt is the time when the stop watch was started. 29 startedAt time.Time 30 // elapsed is the total time measured by the stop watch (i.e. between 31 // all Starts and Stops). 32 elapsed time.Duration 33 // timeSource is the source of time used by the stop watch. It is always 34 // timeutil.Now except for tests. 35 timeSource func() time.Time 36 // cpuStopWatch is used to track CPU usage. It may be nil, in which case any 37 // operations on it are no-ops. 38 cpuStopWatch *cpuStopWatch 39 } 40 } 41 42 // NewStopWatch creates a new StopWatch. 43 func NewStopWatch() *StopWatch { 44 return newStopWatch(Now) 45 } 46 47 // NewStopWatchWithCPU creates a new StopWatch that will track CPU usage in 48 // addition to wall-clock time. 49 func NewStopWatchWithCPU() *StopWatch { 50 w := newStopWatch(Now) 51 if grunning.Supported() { 52 w.mu.cpuStopWatch = &cpuStopWatch{} 53 } 54 return w 55 } 56 57 // NewTestStopWatch create a new StopWatch with the given time source. It is 58 // used for testing only. 59 func NewTestStopWatch(timeSource func() time.Time) *StopWatch { 60 return newStopWatch(timeSource) 61 } 62 63 func newStopWatch(timeSource func() time.Time) *StopWatch { 64 w := &StopWatch{} 65 w.mu.timeSource = timeSource 66 return w 67 } 68 69 // Start starts the stop watch if it hasn't already been started. 70 func (w *StopWatch) Start() { 71 w.mu.Lock() 72 defer w.mu.Unlock() 73 if !w.mu.started { 74 w.mu.started = true 75 w.mu.startedAt = w.mu.timeSource() 76 w.mu.cpuStopWatch.start() 77 } 78 } 79 80 // Stop stops the stop watch if it hasn't already been stopped and accumulates 81 // the duration that elapsed since it was started. If the stop watch has 82 // already been stopped, it is a noop. 83 func (w *StopWatch) Stop() { 84 w.mu.Lock() 85 defer w.mu.Unlock() 86 if w.mu.started { 87 w.mu.started = false 88 w.mu.elapsed += w.mu.timeSource().Sub(w.mu.startedAt) 89 w.mu.cpuStopWatch.stop() 90 } 91 } 92 93 // Elapsed returns the total time measured by the stop watch so far. 94 func (w *StopWatch) Elapsed() time.Duration { 95 w.mu.Lock() 96 defer w.mu.Unlock() 97 return w.mu.elapsed 98 } 99 100 // ElapsedCPU returns the total CPU time measured by the stop watch so far. It 101 // returns zero if cpuStopWatch is nil (which is the case if NewStopWatchWithCPU 102 // was not called or the platform does not support grunning). 103 func (w *StopWatch) ElapsedCPU() time.Duration { 104 w.mu.Lock() 105 defer w.mu.Unlock() 106 return w.mu.cpuStopWatch.elapsed() 107 } 108 109 // LastStartedAt returns the time the stopwatch was last started, and a bool 110 // indicating if the stopwatch is currently started. 111 func (w *StopWatch) LastStartedAt() (startedAt time.Time, started bool) { 112 w.mu.Lock() 113 defer w.mu.Unlock() 114 return w.mu.startedAt, w.mu.started 115 } 116 117 // TestTimeSource is a source of time that remembers when it was created (in 118 // terms of the real time) and returns the time based on its creation time and 119 // the number of "advances" it has had. It is used for testing only. 120 type TestTimeSource struct { 121 initTime time.Time 122 counter int64 123 } 124 125 // NewTestTimeSource create a new TestTimeSource. 126 func NewTestTimeSource() *TestTimeSource { 127 return &TestTimeSource{initTime: Now()} 128 } 129 130 // Now tells the current time according to t. 131 func (t *TestTimeSource) Now() time.Time { 132 return t.initTime.Add(time.Duration(t.counter)) 133 } 134 135 // Advance advances the current time according to t by 1 nanosecond. 136 func (t *TestTimeSource) Advance() { 137 t.counter++ 138 } 139 140 // Elapsed returns how much time has passed since t has been created. Note that 141 // it is equal to the number of advances in nanoseconds. 142 func (t *TestTimeSource) Elapsed() time.Duration { 143 return time.Duration(t.counter) 144 }