refactor timecache

* change clock to `atomic.Int64`
* add `once` fields for starter and stopper
* update dependencies

note: go version updated to 1.19
This commit is contained in:
Širhoe Biazhkovič
2022-11-16 19:59:07 +03:00
parent 566cc0f5b6
commit 0cbc5854cd
6 changed files with 77 additions and 96 deletions
+31 -51
View File
@@ -3,7 +3,7 @@
// 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
// The package runs a global singleton TimeCache that is updated every
// second.
package timecache
@@ -17,12 +17,7 @@ import (
var t *TimeCache
func init() {
t = &TimeCache{
clock: time.Now().UnixNano(),
closed: make(chan struct{}),
running: make(chan struct{}),
}
t = New()
go t.Run(1 * time.Second)
}
@@ -31,46 +26,37 @@ func init() {
type TimeCache struct {
// clock saves the current time's nanoseconds since the Epoch.
// Must be accessed atomically.
clock int64
clock atomic.Int64
closed chan struct{}
running chan struct{}
m sync.Mutex
closed chan struct{}
onceExecutor sync.Once
onceStopper sync.Once
}
// 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{}),
}
func New() (tc *TimeCache) {
tc = &TimeCache{closed: make(chan struct{})}
tc.clock.Store(time.Now().UnixNano())
return
}
// 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())
t.onceExecutor.Do(func() {
t.clock.Store(time.Now().UnixNano()) // refreshing clock after New till next tick
tick := time.NewTicker(interval)
defer tick.Stop()
for {
select {
case <-t.closed:
return
case now := <-tick.C:
t.clock.Store(now.UnixNano())
}
}
}
})
}
// Stop stops the TimeCache.
@@ -78,31 +64,25 @@ func (t *TimeCache) Run(interval time.Duration) {
// 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)
t.onceStopper.Do(func() {
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))
return time.Unix(0, t.clock.Load())
}
// NowUnixNano returns the cached time as nanoseconds since the Unix Epoch.
func (t *TimeCache) NowUnixNano() int64 {
return atomic.LoadInt64(&t.clock)
return t.clock.Load()
}
// 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)
nsec := t.NowUnixNano()
sec := nsec / 1e9
nsec -= sec * 1e9
if nsec < 0 {
@@ -111,17 +91,17 @@ func (t *TimeCache) NowUnix() int64 {
return sec
}
// Now calls Now on the global TimeCache instance.
// Now calls TimeCache.Now on the global TimeCache instance.
func Now() time.Time {
return t.Now()
}
// NowUnixNano calls NowUnixNano on the global TimeCache instance.
// NowUnixNano calls TimeCache.NowUnixNano on the global TimeCache instance.
func NowUnixNano() int64 {
return t.NowUnixNano()
}
// NowUnix calls NowUnix on the global TimeCache instance.
// NowUnix calls TimeCache.NowUnix on the global TimeCache instance.
func NowUnix() int64 {
return t.NowUnix()
}