diff options
Diffstat (limited to '')
-rw-r--r-- | golang-runtime-resume-timers.patch | 187 |
1 files changed, 187 insertions, 0 deletions
diff --git a/golang-runtime-resume-timers.patch b/golang-runtime-resume-timers.patch new file mode 100644 index 00000000..17e09bb3 --- /dev/null +++ b/golang-runtime-resume-timers.patch @@ -0,0 +1,187 @@ +From e7384a38af94ff5e7e748f1424d766e1d3316f8d Mon Sep 17 00:00:00 2001 +From: Jason A. Donenfeld <Jason@zx2c4.com> +Date: Tue, 27 Aug 2019 06:46:16 -0600 +Subject: [PATCH] runtime: monitor for suspend/resume to kick timeouts + +Starting in Windows 8, the wait functions don't take into account +suspend time, even though the monotonic counters do. This results in +timer buckets stalling on resume. Therefore, this commit makes it so +that on resume, we return from the wait functions and recalculate the +amount of time left to wait. + +Fixes: #31528 + +Change-Id: I0db02cc72188cb620954e87a0180e0a3c83f4a56 +--- + +diff --git a/src/runtime/os_windows.go b/src/runtime/os_windows.go +index 074ae0f..6cc9670 100644 +--- a/src/runtime/os_windows.go ++++ b/src/runtime/os_windows.go +@@ -49,6 +49,7 @@ + //go:cgo_import_dynamic runtime._VirtualFree VirtualFree%3 "kernel32.dll" + //go:cgo_import_dynamic runtime._VirtualQuery VirtualQuery%3 "kernel32.dll" + //go:cgo_import_dynamic runtime._WaitForSingleObject WaitForSingleObject%2 "kernel32.dll" ++//go:cgo_import_dynamic runtime._WaitForMultipleObjects WaitForMultipleObjects%4 "kernel32.dll" + //go:cgo_import_dynamic runtime._WriteConsoleW WriteConsoleW%5 "kernel32.dll" + //go:cgo_import_dynamic runtime._WriteFile WriteFile%5 "kernel32.dll" + +@@ -96,6 +97,7 @@ + _VirtualFree, + _VirtualQuery, + _WaitForSingleObject, ++ _WaitForMultipleObjects, + _WriteConsoleW, + _WriteFile, + _ stdFunction +@@ -139,7 +141,8 @@ + func ctrlhandler() + + type mOS struct { +- waitsema uintptr // semaphore for parking on locks ++ waitsema uintptr // semaphore for parking on locks ++ resumesema uintptr // semaphore to indicate suspend/resume + } + + //go:linkname os_sigpipe os.sigpipe +@@ -258,6 +261,34 @@ + } + } + ++func monitorSuspendResume() { ++ powrprof := windowsLoadSystemLib([]byte("powrprof.dll\000")) ++ if powrprof == 0 { ++ return // Running on Windows 7, where we don't need it anyway. ++ } ++ powerRegisterSuspendResumeNotification := windowsFindfunc(powrprof, []byte("PowerRegisterSuspendResumeNotification\000")) ++ if powerRegisterSuspendResumeNotification == nil { ++ return // Running on Windows 7, where we don't need it anyway. ++ } ++ var fn interface{} = func(context uintptr, changeType uint32, setting uintptr) uintptr { ++ lock(&allglock) ++ for _, gp := range allgs { ++ if gp.m != nil && gp.m.resumesema != 0 { ++ stdcall1(_SetEvent, gp.m.resumesema) ++ } ++ } ++ unlock(&allglock) ++ return 0 ++ } ++ var handle uintptr ++ callback := compileCallback(*efaceOf(&fn), true) ++ if stdcall3(powerRegisterSuspendResumeNotification, 2, ++ uintptr(unsafe.Pointer(&[2]uintptr{callback, 0})), ++ uintptr(unsafe.Pointer(&handle))) != 0 { ++ throw("PowerRegisterSuspendResumeNotification failure") ++ } ++} ++ + //go:nosplit + func getLoadLibrary() uintptr { + return uintptr(unsafe.Pointer(_LoadLibraryW)) +@@ -488,6 +519,10 @@ + } + + stdcall1(_FreeEnvironmentStringsW, uintptr(strings)) ++ ++ // We call this all the way here, late in init, so that malloc works ++ // for the callback function this generates. ++ monitorSuspendResume() + } + + // exiting is set to non-zero when the process is exiting. +@@ -606,21 +641,34 @@ + _WAIT_FAILED = 0xFFFFFFFF + ) + +- // store ms in ns to save stack space ++ var result uintptr ++ var elapsed, start int64 ++retry: + if ns < 0 { +- ns = _INFINITE ++ result = stdcall2(_WaitForSingleObject, getg().m.waitsema, uintptr(_INFINITE)) + } else { +- ns = int64(timediv(ns, 1000000, nil)) +- if ns == 0 { +- ns = 1 ++ if start == 0 { ++ start = nanotime() + } ++ ms := int64(timediv(ns-elapsed, 1000000, nil)) ++ if ms == 0 { ++ ms = 1 ++ } ++ result = stdcall4(_WaitForMultipleObjects, 2, ++ uintptr(unsafe.Pointer(&[2]uintptr{getg().m.waitsema, getg().m.resumesema})), ++ 0, uintptr(ms)) + } +- +- result := stdcall2(_WaitForSingleObject, getg().m.waitsema, uintptr(ns)) + switch result { + case _WAIT_OBJECT_0: //signaled + return 0 + ++ case _WAIT_OBJECT_0 + 1: //system resume ++ elapsed = nanotime() - start ++ if elapsed >= ns { ++ return -1 ++ } ++ goto retry ++ + case _WAIT_TIMEOUT: + return -1 + +@@ -667,6 +715,15 @@ + throw("runtime.semacreate") + }) + } ++ mp.resumesema = stdcall4(_CreateEventA, 0, 0, 0, 0) ++ if mp.resumesema == 0 { ++ systemstack(func() { ++ print("runtime: createevent failed; errno=", getlasterror(), "\n") ++ throw("runtime.semacreate") ++ }) ++ stdcall1(_CloseHandle, mp.waitsema) ++ mp.waitsema = 0 ++ } + } + + // May run with m.p==nil, so write barriers are not allowed. This +diff --git a/src/runtime/syscall_windows.go b/src/runtime/syscall_windows.go +index 722a73d..0e2fcfb 100644 +--- a/src/runtime/syscall_windows.go ++++ b/src/runtime/syscall_windows.go +@@ -74,16 +74,18 @@ + argsize += uintptrSize + } + +- lock(&cbs.lock) +- defer unlock(&cbs.lock) ++ lock(&cbs.lock) // We don't unlock this in a defer because this is used from the system stack. + + n := cbs.n + for i := 0; i < n; i++ { + if cbs.ctxt[i].gobody == fn.data && cbs.ctxt[i].isCleanstack() == cleanstack { +- return callbackasmAddr(i) ++ r := callbackasmAddr(i) ++ unlock(&cbs.lock) ++ return r + } + } + if n >= cb_max { ++ unlock(&cbs.lock) + throw("too many callback functions") + } + +@@ -99,7 +101,9 @@ + cbs.ctxt[n] = c + cbs.n++ + +- return callbackasmAddr(n) ++ r := callbackasmAddr(n) ++ unlock(&cbs.lock) ++ return r + } + + const _LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800 |