diff options
author | Daniel Martí <mvdan@mvdan.cc> | 2019-09-02 14:34:20 +0000 |
---|---|---|
committer | Daniel Martí <mvdan@mvdan.cc> | 2019-09-02 21:46:03 +0000 |
commit | b36a7a502a590bd9fbf7f73b9678ba58028acfde (patch) | |
tree | edb453c155a75639cc5b5d0091e6ab98703da1e2 /src/runtime/os_windows.go | |
parent | ea2fb678c0c0af7be23e0149532f1cd9c8858cdf (diff) | |
download | go-b36a7a502a590bd9fbf7f73b9678ba58028acfde.tar.gz go-b36a7a502a590bd9fbf7f73b9678ba58028acfde.zip |
Revert "runtime: remove slow time compatibility hacks for wine"
This reverts CL 191759.
Reason for revert: broke most Go programs using the time package on Wine,
including on 4.15, the latest stable version. Only wine-staging (with
experimental patches) contains an upstream fix we could rely on.
Change-Id: Ic8ba126022e54f412174042fbb9abed82d5eb318
Reviewed-on: https://go-review.googlesource.com/c/go/+/192622
Run-TryBot: Daniel Martí <mvdan@mvdan.cc>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Alex Brainman <alex.brainman@gmail.com>
Diffstat (limited to 'src/runtime/os_windows.go')
-rw-r--r-- | src/runtime/os_windows.go | 78 |
1 files changed, 78 insertions, 0 deletions
diff --git a/src/runtime/os_windows.go b/src/runtime/os_windows.go index 2a2b5fa122..cd9e98914c 100644 --- a/src/runtime/os_windows.go +++ b/src/runtime/os_windows.go @@ -76,10 +76,12 @@ var ( _GetStdHandle, _GetSystemDirectoryA, _GetSystemInfo, + _GetSystemTimeAsFileTime, _GetThreadContext, _LoadLibraryW, _LoadLibraryA, _QueryPerformanceCounter, + _QueryPerformanceFrequency, _ResumeThread, _SetConsoleCtrlHandler, _SetErrorMode, @@ -252,6 +254,11 @@ func loadOptionalSyscalls() { if _WSAGetOverlappedResult == nil { throw("WSAGetOverlappedResult not found") } + + if windowsFindfunc(n32, []byte("wine_get_version\000")) != nil { + // running on Wine + initWine(k32) + } } func monitorSuspendResume() { @@ -411,6 +418,77 @@ func osinit() { func nanotime() int64 +// useQPCTime controls whether time.now and nanotime use QueryPerformanceCounter. +// This is only set to 1 when running under Wine. +var useQPCTime uint8 + +var qpcStartCounter int64 +var qpcMultiplier int64 + +//go:nosplit +func nanotimeQPC() int64 { + var counter int64 = 0 + stdcall1(_QueryPerformanceCounter, uintptr(unsafe.Pointer(&counter))) + + // returns number of nanoseconds + return (counter - qpcStartCounter) * qpcMultiplier +} + +//go:nosplit +func nowQPC() (sec int64, nsec int32, mono int64) { + var ft int64 + stdcall1(_GetSystemTimeAsFileTime, uintptr(unsafe.Pointer(&ft))) + + t := (ft - 116444736000000000) * 100 + + sec = t / 1000000000 + nsec = int32(t - sec*1000000000) + + mono = nanotimeQPC() + return +} + +func initWine(k32 uintptr) { + _GetSystemTimeAsFileTime = windowsFindfunc(k32, []byte("GetSystemTimeAsFileTime\000")) + if _GetSystemTimeAsFileTime == nil { + throw("could not find GetSystemTimeAsFileTime() syscall") + } + + _QueryPerformanceCounter = windowsFindfunc(k32, []byte("QueryPerformanceCounter\000")) + _QueryPerformanceFrequency = windowsFindfunc(k32, []byte("QueryPerformanceFrequency\000")) + if _QueryPerformanceCounter == nil || _QueryPerformanceFrequency == nil { + throw("could not find QPC syscalls") + } + + // We can not simply fallback to GetSystemTimeAsFileTime() syscall, since its time is not monotonic, + // instead we use QueryPerformanceCounter family of syscalls to implement monotonic timer + // https://msdn.microsoft.com/en-us/library/windows/desktop/dn553408(v=vs.85).aspx + + var tmp int64 + stdcall1(_QueryPerformanceFrequency, uintptr(unsafe.Pointer(&tmp))) + if tmp == 0 { + throw("QueryPerformanceFrequency syscall returned zero, running on unsupported hardware") + } + + // This should not overflow, it is a number of ticks of the performance counter per second, + // its resolution is at most 10 per usecond (on Wine, even smaller on real hardware), so it will be at most 10 millions here, + // panic if overflows. + if tmp > (1<<31 - 1) { + throw("QueryPerformanceFrequency overflow 32 bit divider, check nosplit discussion to proceed") + } + qpcFrequency := int32(tmp) + stdcall1(_QueryPerformanceCounter, uintptr(unsafe.Pointer(&qpcStartCounter))) + + // Since we are supposed to run this time calls only on Wine, it does not lose precision, + // since Wine's timer is kind of emulated at 10 Mhz, so it will be a nice round multiplier of 100 + // but for general purpose system (like 3.3 Mhz timer on i7) it will not be very precise. + // We have to do it this way (or similar), since multiplying QPC counter by 100 millions overflows + // int64 and resulted time will always be invalid. + qpcMultiplier = int64(timediv(1000000000, qpcFrequency, nil)) + + useQPCTime = 1 +} + //go:nosplit func getRandomData(r []byte) { n := 0 |