127 lines
2.8 KiB
Go
127 lines
2.8 KiB
Go
// Package timecache provides a cache for the system clock, to avoid calls to
|
|
// time.Now().
|
|
// The time is stored as one int64 which holds the number of nanoseconds since
|
|
// the Unix Epoch. The value is accessed using atomic primitives, without
|
|
// locking.
|
|
// The package runs a global singleton TimeCache that is is updated every
|
|
// second.
|
|
package timecache
|
|
|
|
import (
|
|
"sync"
|
|
"sync/atomic"
|
|
"time"
|
|
)
|
|
|
|
// t is the global TimeCache.
|
|
var t *TimeCache
|
|
|
|
func init() {
|
|
t = &TimeCache{
|
|
clock: time.Now().UnixNano(),
|
|
closed: make(chan struct{}),
|
|
running: make(chan struct{}),
|
|
}
|
|
|
|
go t.Run(1 * time.Second)
|
|
}
|
|
|
|
// A TimeCache is a cache for the current system time.
|
|
// The cached time has nanosecond precision.
|
|
type TimeCache struct {
|
|
// clock saves the current time's nanoseconds since the Epoch.
|
|
// Must be accessed atomically.
|
|
clock int64
|
|
|
|
closed chan struct{}
|
|
running chan struct{}
|
|
m sync.Mutex
|
|
}
|
|
|
|
// New returns a new TimeCache instance.
|
|
// The TimeCache must be started to update the time.
|
|
func New() *TimeCache {
|
|
return &TimeCache{
|
|
clock: time.Now().UnixNano(),
|
|
closed: make(chan struct{}),
|
|
running: make(chan struct{}),
|
|
}
|
|
}
|
|
|
|
// Run runs the TimeCache, updating the cached clock value once every interval
|
|
// and blocks until Stop is called.
|
|
func (t *TimeCache) Run(interval time.Duration) {
|
|
t.m.Lock()
|
|
select {
|
|
case <-t.running:
|
|
panic("Run called multiple times")
|
|
default:
|
|
}
|
|
close(t.running)
|
|
t.m.Unlock()
|
|
|
|
tick := time.NewTicker(interval)
|
|
defer tick.Stop()
|
|
for {
|
|
select {
|
|
case <-t.closed:
|
|
tick.Stop()
|
|
return
|
|
case now := <-tick.C:
|
|
atomic.StoreInt64(&t.clock, now.UnixNano())
|
|
}
|
|
}
|
|
}
|
|
|
|
// Stop stops the TimeCache.
|
|
// The cached time remains valid but will not be updated anymore.
|
|
// A TimeCache can not be restarted. Construct a new one instead.
|
|
// Calling Stop again is a no-op.
|
|
func (t *TimeCache) Stop() {
|
|
t.m.Lock()
|
|
defer t.m.Unlock()
|
|
|
|
select {
|
|
case <-t.closed:
|
|
return
|
|
default:
|
|
}
|
|
close(t.closed)
|
|
}
|
|
|
|
// Now returns the cached time as a time.Time value.
|
|
func (t *TimeCache) Now() time.Time {
|
|
return time.Unix(0, atomic.LoadInt64(&t.clock))
|
|
}
|
|
|
|
// NowUnixNano returns the cached time as nanoseconds since the Unix Epoch.
|
|
func (t *TimeCache) NowUnixNano() int64 {
|
|
return atomic.LoadInt64(&t.clock)
|
|
}
|
|
|
|
// NowUnix returns the cached time as seconds since the Unix Epoch.
|
|
func (t *TimeCache) NowUnix() int64 {
|
|
// Adopted from time.Unix
|
|
nsec := atomic.LoadInt64(&t.clock)
|
|
sec := nsec / 1e9
|
|
nsec -= sec * 1e9
|
|
if nsec < 0 {
|
|
sec--
|
|
}
|
|
return sec
|
|
}
|
|
|
|
// Now calls Now on the global TimeCache instance.
|
|
func Now() time.Time {
|
|
return t.Now()
|
|
}
|
|
|
|
// NowUnixNano calls NowUnixNano on the global TimeCache instance.
|
|
func NowUnixNano() int64 {
|
|
return t.NowUnixNano()
|
|
}
|
|
|
|
// NowUnix calls NowUnix on the global TimeCache instance.
|
|
func NowUnix() int64 {
|
|
return t.NowUnix()
|
|
}
|