go.mway.dev/chrono@v0.6.1-0.20240126030049-189c5aef20d2/clock/clock.go (about)

     1  // Copyright (c) 2023 Matt Way
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to
     5  // deal in the Software without restriction, including without limitation the
     6  // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
     7  // sell copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    18  // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
    19  // IN THE THE SOFTWARE.
    20  
    21  // Package clock provides clock-related types and utilities.
    22  package clock
    23  
    24  import (
    25  	"errors"
    26  	"time"
    27  )
    28  
    29  // ErrNoClockFunc is returned when creating a new [Clock] without a valid
    30  // [TimeFunc] or [NanotimeFunc].
    31  var ErrNoClockFunc = errors.New("no clock function provided")
    32  
    33  // A Clock tells time.
    34  type Clock interface {
    35  	// After waits for the duration to elapse and then sends the current time
    36  	// on the returned channel. It is equivalent to NewTimer(d).C. The
    37  	// underlying [Timer] is not recovered by the garbage collector until the
    38  	// it fires. If efficiency is a concern, use [NewTimer] instead and call
    39  	// [Timer.Stop] if the timer is no longer needed.
    40  	After(d time.Duration) <-chan time.Time
    41  
    42  	// AfterFunc waits for the duration to elapse and then calls fn in its own
    43  	// goroutine. It returns a [Timer] that can be used to cancel the call using
    44  	// its [Timer.Stop] method.
    45  	AfterFunc(d time.Duration, fn func()) *Timer
    46  
    47  	// Nanotime returns the current time in nanoseconds.
    48  	Nanotime() int64
    49  
    50  	// NewStopwatch returns a new [Stopwatch] that uses the [Clock] for
    51  	// measuring time.
    52  	NewStopwatch() *Stopwatch
    53  
    54  	// NewTicker returns a new [Ticker] containing a channel that will send the
    55  	// current time on the channel after each tick. The period of the ticks is
    56  	// specified by the duration argument. The ticker will adjust the time
    57  	// interval or drop ticks to make up for slow receivers. The duration d
    58  	// must be greater than zero; if not, NewTicker will panic. Stop the ticker
    59  	// to release associated resources.
    60  	NewTicker(d time.Duration) *Ticker
    61  
    62  	// NewTimer creates a new [Timer] that will send the current time on its
    63  	// channel after at least d has elapsed.
    64  	NewTimer(d time.Duration) *Timer
    65  
    66  	// Now returns the current time. For wall clocks, this is the local time;
    67  	// for monotonic clocks, this is the system's monotonic time. Other Clock
    68  	// implementations may have different locale or clock time semantics.
    69  	Now() time.Time
    70  
    71  	// Since returns the time elapsed since t. It is shorthand for
    72  	// Now().Sub(t).
    73  	Since(t time.Time) time.Duration
    74  
    75  	// Since returns the time elapsed since ns. It is shorthand for
    76  	// Nanotime()-ns.
    77  	SinceNanotime(ns int64) time.Duration
    78  
    79  	// Sleep pauses the current goroutine for at least the duration d. A
    80  	// negative or zero duration causes Sleep to return immediately.
    81  	Sleep(d time.Duration)
    82  
    83  	// Tick is a convenience wrapper for [NewTicker] providing access to the
    84  	// ticking channel only. While Tick is useful for clients that have no need
    85  	// to shut down the [Ticker], be aware that without a way to shut it down
    86  	// the underlying Ticker cannot be recovered by the garbage collector; it
    87  	// "leaks". Like [NewTicker], Tick will panic if d <= 0.
    88  	Tick(time.Duration) <-chan time.Time
    89  }
    90  
    91  // NewClock returns a new [Clock] based on the given options.
    92  func NewClock(opts ...Option) (Clock, error) {
    93  	options := DefaultOptions()
    94  	for _, opt := range opts {
    95  		opt.apply(&options)
    96  	}
    97  
    98  	if options.NanotimeFunc != nil {
    99  		return newMonotonicClock(options.NanotimeFunc), nil
   100  	}
   101  
   102  	return newWallClock(options.TimeFunc), nil
   103  }
   104  
   105  // MustClock panics if the given error is not nil, otherwise it returns the
   106  // given [Clock].
   107  func MustClock(clock Clock, err error) Clock {
   108  	if err != nil {
   109  		panic(err)
   110  	}
   111  
   112  	return clock
   113  }
   114  
   115  // NewMonotonicClock returns a new monotonic [Clock].
   116  func NewMonotonicClock() Clock {
   117  	return MustClock(NewClock(WithNanotimeFunc(DefaultNanotimeFunc())))
   118  }
   119  
   120  // NewWallClock returns a new wall [Clock].
   121  func NewWallClock() Clock {
   122  	return MustClock(NewClock(WithTimeFunc(DefaultTimeFunc())))
   123  }