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  }