aboutsummaryrefslogtreecommitdiff
path: root/src/runtime/os_windows.go
diff options
context:
space:
mode:
authorDaniel Martí <mvdan@mvdan.cc>2019-09-02 14:34:20 +0000
committerDaniel Martí <mvdan@mvdan.cc>2019-09-02 21:46:03 +0000
commitb36a7a502a590bd9fbf7f73b9678ba58028acfde (patch)
treeedb453c155a75639cc5b5d0091e6ab98703da1e2 /src/runtime/os_windows.go
parentea2fb678c0c0af7be23e0149532f1cd9c8858cdf (diff)
downloadgo-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.go78
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