wasi: support nanosec precision in monotonic clock on Windows (#1123)

Signed-off-by: Takeshi Yoneda <takeshi@tetrate.io>
This commit is contained in:
Takeshi Yoneda
2023-02-13 22:28:12 -08:00
committed by GitHub
parent f84a68b576
commit 677ed399a1
5 changed files with 62 additions and 2 deletions

View File

@@ -177,6 +177,32 @@ func Test_clockTimeGet(t *testing.T) {
}
}
// Similar to https://github.com/WebAssembly/wasi-testsuite/blob/dc7f8d27be1030cd4788ebdf07d9b57e5d23441e/tests/c/testsuite/clock_gettime-monotonic.c
func Test_clockTimeGet_monotonic(t *testing.T) {
mod, r, _ := requireProxyModule(t, wazero.NewModuleConfig().
// Important not to use fake time!
WithSysNanotime())
defer r.Close(testCtx)
getMonotonicTime := func() uint64 {
const offset uint32 = 0
requireErrno(t, ErrnoSuccess, mod, ClockTimeGetName, uint64(ClockIDMonotonic),
0 /* TODO: precision */, uint64(offset))
timestamp, ok := mod.Memory().ReadUint64Le(offset)
require.True(t, ok)
return timestamp
}
t1 := getMonotonicTime()
t2 := getMonotonicTime()
t3 := getMonotonicTime()
t4 := getMonotonicTime()
require.True(t, t1 < t2)
require.True(t, t2 < t3)
require.True(t, t3 < t4)
}
func Test_clockTimeGet_Unsupported(t *testing.T) {
mod, r, log := requireProxyModule(t, wazero.NewModuleConfig())
defer r.Close(testCtx)

View File

@@ -1,4 +1,4 @@
//go:build cgo
//go:build cgo && !windows
package platform

View File

@@ -1,4 +1,4 @@
//go:build !cgo
//go:build !cgo && !windows
package platform

View File

@@ -65,6 +65,13 @@ func Test_Nanotime(t *testing.T) {
}
}
func Test_Nanotime_ensure_monotonic(t *testing.T) {
n1 := Nanotime()
time.Sleep(time.Millisecond)
n2 := Nanotime()
require.True(t, n1 < n2)
}
func Test_Nanosleep(t *testing.T) {
// In CI, Nanosleep(50ms) returned after 197ms.
// As we can't control the platform clock, we have to be lenient

View File

@@ -0,0 +1,27 @@
//go:build windows
package platform
import (
"unsafe"
)
var qpc = kernel32.NewProc("QueryPerformanceCounter")
// On Windows, time.Time handled in time package cannot have the nanosecond precision.
// The reason is that by default, it doesn't use QueryPerformanceCounter[1], but instead, use "interrupt time"
// which doesn't support nanoseconds precision (though it is a monotonic) [2, 3, 4, 5].
//
// [1] https://learn.microsoft.com/en-us/windows/win32/api/profileapi/nf-profileapi-queryperformancecounter
// [2] https://github.com/golang/go/blob/0cd309e12818f988693bf8e4d9f1453331dcf9f2/src/runtime/sys_windows_amd64.s#L297-L298
// [3] https://github.com/golang/go/blob/0cd309e12818f988693bf8e4d9f1453331dcf9f2/src/runtime/os_windows.go#L549-L551
// [4] https://github.com/golang/go/blob/master/src/runtime/time_windows.h#L7-L13
// [5] http://web.archive.org/web/20210411000829/https://wrkhpi.wordpress.com/2007/08/09/getting-os-information-the-kuser_shared_data-structure/
//
// Therefore, on Windows, we directly invoke the syscall for QPC instead of time.Now or runtime.nanotime.
// See https://github.com/golang/go/issues/31160 for example.
func nanotime() int64 {
var now int64
_, _, _ = qpc.Call(uintptr(unsafe.Pointer(&now)))
return now
}