runtime: fix time base for time.Now()

This function previously returned the atomic time, that isn't affected
by system time changes but also has a time base at some arbitrary time
in the past. This makes sense for baremetal platforms (which typically
don't know the wall time) but it gives surprising results on Linux and
macOS: time.Now() usually returns a time somewhere near the start of
1970.

This commit fixes this by obtaining both time values: the monotonic time
and the wall clock time. This is also how the Go runtime implements the
time.now function.
Этот коммит содержится в:
Ayke van Laethem 2021-07-20 13:21:25 +02:00 коммит произвёл Ron Evans
родитель 73cf187552
коммит 03481789b0
6 изменённых файлов: 58 добавлений и 28 удалений

Просмотреть файл

@ -52,3 +52,24 @@ func syscall_Exit(code int) {
}
const baremetal = true
// timeOffset is how long the monotonic clock started after the Unix epoch. It
// should be a positive integer under normal operation or zero when it has not
// been set.
var timeOffset int64
//go:linkname now time.now
func now() (sec int64, nsec int32, mono int64) {
mono = nanotime()
sec = (mono + timeOffset) / (1000 * 1000 * 1000)
nsec = int32((mono + timeOffset) - sec*(1000*1000*1000))
return
}
// AdjustTimeOffset adds the given offset to the built-in time offset. A
// positive value adds to the time (skipping some time), a negative value moves
// the clock into the past.
func AdjustTimeOffset(offset int64) {
// TODO: do this atomically?
timeOffset += offset
}

Просмотреть файл

@ -11,3 +11,9 @@ const (
flag_MAP_PRIVATE = 0x2
flag_MAP_ANONYMOUS = 0x1000 // MAP_ANON
)
// Source: https://opensource.apple.com/source/Libc/Libc-1439.100.3/include/time.h.auto.html
const (
clock_REALTIME = 0
clock_MONOTONIC_RAW = 4
)

Просмотреть файл

@ -11,3 +11,9 @@ const (
flag_MAP_PRIVATE = 0x2
flag_MAP_ANONYMOUS = 0x20
)
// Source: https://github.com/torvalds/linux/blob/master/include/uapi/linux/time.h
const (
clock_REALTIME = 0
clock_MONOTONIC_RAW = 4
)

Просмотреть файл

@ -68,27 +68,6 @@ func nanotime() int64 {
return ticksToNanoseconds(ticks())
}
// timeOffset is how long the monotonic clock started after the Unix epoch. It
// should be a positive integer under normal operation or zero when it has not
// been set.
var timeOffset int64
//go:linkname now time.now
func now() (sec int64, nsec int32, mono int64) {
mono = nanotime()
sec = (mono + timeOffset) / (1000 * 1000 * 1000)
nsec = int32((mono + timeOffset) - sec*(1000*1000*1000))
return
}
// AdjustTimeOffset adds the given offset to the built-in time offset. A
// positive value adds to the time (skipping some time), a negative value moves
// the clock into the past.
func AdjustTimeOffset(offset int64) {
// TODO: do this atomically?
timeOffset += offset
}
// Copied from the Go runtime source code.
//go:linkname os_sigpipe os.sigpipe
func os_sigpipe() {

Просмотреть файл

@ -50,6 +50,14 @@ func putchar(c byte) {
}
}
//go:linkname now time.now
func now() (sec int64, nsec int32, mono int64) {
mono = nanotime()
sec = mono / (1000 * 1000 * 1000)
nsec = int32(mono - sec*(1000*1000*1000))
return
}
// Abort executes the wasm 'unreachable' instruction.
func abort() {
trap()

Просмотреть файл

@ -38,8 +38,6 @@ type timespec struct {
tv_nsec int // long: on Linux and macOS, follows the platform bitness
}
const CLOCK_MONOTONIC_RAW = 4
var stackTop uintptr
func postinit() {}
@ -138,19 +136,31 @@ func sleepTicks(d timeUnit) {
usleep(uint(d) / 1000)
}
// Return monotonic time in nanoseconds.
//
// TODO: noescape
func monotime() uint64 {
func getTime(clock int32) uint64 {
ts := timespec{}
clock_gettime(CLOCK_MONOTONIC_RAW, &ts)
clock_gettime(clock, &ts)
return uint64(ts.tv_sec)*1000*1000*1000 + uint64(ts.tv_nsec)
}
// Return monotonic time in nanoseconds.
func monotime() uint64 {
return getTime(clock_MONOTONIC_RAW)
}
func ticks() timeUnit {
return timeUnit(monotime())
}
//go:linkname now time.now
func now() (sec int64, nsec int32, mono int64) {
ts := timespec{}
clock_gettime(clock_REALTIME, &ts)
sec = int64(ts.tv_sec)
nsec = int32(ts.tv_nsec)
mono = nanotime()
return
}
//go:linkname syscall_Exit syscall.Exit
func syscall_Exit(code int) {
exit(code)