From 03481789b0bbc83b9381b3aec32c87965f8158c2 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Tue, 20 Jul 2021 13:21:25 +0200 Subject: [PATCH] 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. --- src/runtime/baremetal.go | 21 +++++++++++++++++++++ src/runtime/os_darwin.go | 6 ++++++ src/runtime/os_linux.go | 6 ++++++ src/runtime/runtime.go | 21 --------------------- src/runtime/runtime_tinygowasm.go | 8 ++++++++ src/runtime/runtime_unix.go | 24 +++++++++++++++++------- 6 files changed, 58 insertions(+), 28 deletions(-) diff --git a/src/runtime/baremetal.go b/src/runtime/baremetal.go index ce69e338..5abd1371 100644 --- a/src/runtime/baremetal.go +++ b/src/runtime/baremetal.go @@ -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 +} diff --git a/src/runtime/os_darwin.go b/src/runtime/os_darwin.go index ac31b67d..be878909 100644 --- a/src/runtime/os_darwin.go +++ b/src/runtime/os_darwin.go @@ -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 +) diff --git a/src/runtime/os_linux.go b/src/runtime/os_linux.go index 7613134e..aa056173 100644 --- a/src/runtime/os_linux.go +++ b/src/runtime/os_linux.go @@ -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 +) diff --git a/src/runtime/runtime.go b/src/runtime/runtime.go index 7b1b4f5a..55102411 100644 --- a/src/runtime/runtime.go +++ b/src/runtime/runtime.go @@ -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() { diff --git a/src/runtime/runtime_tinygowasm.go b/src/runtime/runtime_tinygowasm.go index 989fbb80..80eaa8d3 100644 --- a/src/runtime/runtime_tinygowasm.go +++ b/src/runtime/runtime_tinygowasm.go @@ -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() diff --git a/src/runtime/runtime_unix.go b/src/runtime/runtime_unix.go index c185c4ab..464d8dfe 100644 --- a/src/runtime/runtime_unix.go +++ b/src/runtime/runtime_unix.go @@ -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)