1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
|
package nowish
import (
"sync"
"sync/atomic"
"time"
"unsafe"
)
// Start returns a new Clock instance initialized and
// started with the provided precision, along with the
// stop function for it's underlying timer
func Start(precision time.Duration) (*Clock, func()) {
c := Clock{}
return &c, c.Start(precision)
}
type Clock struct {
// format stores the time formatting style string
format string
// valid indicates whether the current value stored in .Format is valid
valid uint32
// mutex protects writes to .Format, not because it would be unsafe, but
// because we want to minimize unnnecessary allocations
mutex sync.Mutex
// nowfmt is an unsafe pointer to the last-updated time format string
nowfmt unsafe.Pointer
// now is an unsafe pointer to the last-updated time.Time object
now unsafe.Pointer
}
// Start starts the clock with the provided precision, the returned
// function is the stop function for the underlying timer. For >= 2ms,
// actual precision is usually within AT LEAST 10% of requested precision,
// less than this and the actual precision very quickly deteriorates.
func (c *Clock) Start(precision time.Duration) func() {
// Create ticker from duration
tick := time.NewTicker(precision / 10)
// Set initial time
t := time.Now()
atomic.StorePointer(&c.now, unsafe.Pointer(&t))
// Set initial format
s := ""
atomic.StorePointer(&c.nowfmt, unsafe.Pointer(&s))
// If formatting string unset, set default
c.mutex.Lock()
if c.format == "" {
c.format = time.RFC822
}
c.mutex.Unlock()
// Start main routine
go c.run(tick)
// Return stop fn
return tick.Stop
}
// run is the internal clock ticking loop.
func (c *Clock) run(tick *time.Ticker) {
for {
// Wait on tick
_, ok := <-tick.C
// Channel closed
if !ok {
break
}
// Update time
t := time.Now()
atomic.StorePointer(&c.now, unsafe.Pointer(&t))
// Invalidate format string
atomic.StoreUint32(&c.valid, 0)
}
}
// Now returns a good (ish) estimate of the current 'now' time.
func (c *Clock) Now() time.Time {
return *(*time.Time)(atomic.LoadPointer(&c.now))
}
// NowFormat returns the formatted "now" time, cached until next tick and "now" updates.
func (c *Clock) NowFormat() string {
// If format still valid, return this
if atomic.LoadUint32(&c.valid) == 1 {
return *(*string)(atomic.LoadPointer(&c.nowfmt))
}
// Get mutex lock
c.mutex.Lock()
// Double check still invalid
if atomic.LoadUint32(&c.valid) == 1 {
c.mutex.Unlock()
return *(*string)(atomic.LoadPointer(&c.nowfmt))
}
// Calculate time format
nowfmt := c.Now().Format(c.format)
// Update the stored value and set valid!
atomic.StorePointer(&c.nowfmt, unsafe.Pointer(&nowfmt))
atomic.StoreUint32(&c.valid, 1)
// Unlock and return
c.mutex.Unlock()
return nowfmt
}
// SetFormat sets the time format string used by .NowFormat().
func (c *Clock) SetFormat(format string) {
// Get mutex lock
c.mutex.Lock()
// Update time format
c.format = format
// Invalidate current format string
atomic.StoreUint32(&c.valid, 0)
// Unlock
c.mutex.Unlock()
}
|