github.com/coyove/common@v0.0.0-20240403014525-f70e643f9de8/clock/clock.go (about) 1 package clock 2 3 import ( 4 "runtime" 5 "sync/atomic" 6 "time" 7 "unsafe" 8 ) 9 10 const hasMonotonic = 1 << 63 11 12 var ( 13 counter int64 = -1 14 start int64 15 lastSec int64 16 ) 17 18 func init() { 19 x := time.Now() 20 s := *(*[2]uint64)(unsafe.Pointer(&x)) 21 if s[0]&hasMonotonic == 1 { 22 panic("monotonic clock not found on platform: " + runtime.GOOS + "/" + runtime.GOARCH) 23 } 24 start = x.Unix() 25 lastSec = start 26 } 27 28 func timeNow() (int64, int64) { 29 x := time.Now() 30 s := *(*[2]int64)(unsafe.Pointer(&x)) 31 // s[1] -> time.Time.ext -> nsec since process started 32 33 sec := start + s[1]/1e9 34 ctr := atomic.AddInt64(&counter, 1) 35 36 if atomic.SwapInt64(&lastSec, sec) != sec { 37 // We have crossed a full second, so we will try to reset the counter 38 // Only one caller will successfully swap the value to 0, the rest will do the atomic adding 39 if atomic.CompareAndSwapInt64(&counter, atomic.LoadInt64(&counter), 0) { 40 ctr = 0 41 } else { 42 ctr = atomic.AddInt64(&counter, 1) 43 } 44 } 45 46 if ctr&0xffffff != ctr { 47 // Worst case, the local machine is so fast that 16M values is just not enough for the counter 48 // We have to manually delay the whole process by sleeping 49 time.Sleep(time.Millisecond * 10) 50 return timeNow() 51 } 52 53 // 24bits for the counter, which allow ~16M effective values 54 return sec, sec<<24 | (ctr & 0xffffff) 55 } 56 57 // Timestamp returns a timestamp that is guaranteed to be 58 // goroutine-safe and globally unique on this machine as long as the process persists 59 func Timestamp() int64 { 60 _, v := timeNow() 61 return v 62 } 63 64 // Unix returns the unix timestamp based on when the process started 65 // so its returned value will not affected by the changing of system wall timer 66 func Unix() int64 { 67 v, _ := timeNow() 68 return v 69 }