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  }