aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/golang-runtime-resume-timers.patch
diff options
context:
space:
mode:
Diffstat (limited to 'golang-runtime-resume-timers.patch')
-rw-r--r--golang-runtime-resume-timers.patch187
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