forked from glycerine/sshego
/
mono.go
60 lines (53 loc) · 1.39 KB
/
mono.go
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
package ssh
import (
"time"
_ "unsafe" // required to use //go:linkname
)
//go:noescape
//go:linkname nanotime runtime.nanotime
func nanotime() int64
// monoNow provides a read from a monotonic clock that has
// an arbitrary but consistent start point.
func monoNow() int64 {
return nanotime()
}
// subject to error due to clock adjustment
// in the past, but avoids error due to clock
// adjustment in the future.
func addMono(tm time.Time) time.Time {
if tm.IsZero() {
return tm // leave zero alone
}
if tm.Round(0) != tm {
return tm // already has monotonic part
}
now := time.Now() // has monotonic part, as of go1.9
unow := now.UnixNano()
then := tm.UnixNano()
diff := then - unow
return now.Add(time.Duration(diff))
}
func getMono(tm time.Time) uint64 {
if tm.IsZero() {
panic("cannot call getMono on a zero time")
}
now := time.Now()
mnow := nanotime()
unow := now.UnixNano()
return uint64(mnow - (unow - tm.UnixNano()))
}
// monoToTime is only an approximation. It is
// only approximate down to ~ 1 usec because Go doesn't
// expose the monotonic timestamp directly, so we
// have to get it approximately by assuming two
// sequential calls to nanotime() and time.Now()
// return the same.
//
func monoToTime(x int64) time.Time {
now := time.Now()
mnow := nanotime()
return now.Add(time.Duration(mnow - int64(x)))
}
func stripMono(tm time.Time) time.Time {
return tm.Round(0)
}