Skip to content

Commit 1f354a6

Browse files
qmuntalgopherbot
authored andcommitted
runtime: don't call lockOSThread for every syscall call on Windows
Windows syscall.SyscallN currently calls lockOSThread for every syscall. This can be expensive and produce unnecessary context switches, especially when the syscall is called frequently under high contention. The lockOSThread was necessary to ensure that cgocall wouldn't reschedule the goroutine to a different M, as the syscall return values are reported back in the M struct. This CL instructs cgocall to copy the syscall return values into the the M that will see the caller on return, so the caller no longer needs to call lockOSThread. Updates #58336. Cq-Include-Trybots: luci.golang.try:gotip-windows-arm64,gotip-windows-amd64-longtest Change-Id: If6644fd111dbacab74e7dcee2afa18ca146735da Reviewed-on: https://go-review.googlesource.com/c/go/+/562915 Reviewed-by: Alex Brainman <[email protected]> Auto-Submit: Emmanuel Odeke <[email protected]> Reviewed-by: Than McIntosh <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Reviewed-by: Michael Pratt <[email protected]> Run-TryBot: Emmanuel Odeke <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent 3659b87 commit 1f354a6

File tree

5 files changed

+27
-12
lines changed

5 files changed

+27
-12
lines changed

src/runtime/cgocall.go

+9-3
Original file line numberDiff line numberDiff line change
@@ -181,8 +181,14 @@ func cgocall(fn, arg unsafe.Pointer) int32 {
181181

182182
osPreemptExtExit(mp)
183183

184+
// Save current syscall parameters, so m.winsyscall can be
185+
// used again if callback decide to make syscall.
186+
winsyscall := mp.winsyscall
187+
184188
exitsyscall()
185189

190+
getg().m.winsyscall = winsyscall
191+
186192
// Note that raceacquire must be called only after exitsyscall has
187193
// wired this M to a P.
188194
if raceenabled {
@@ -297,9 +303,9 @@ func cgocallbackg(fn, frame unsafe.Pointer, ctxt uintptr) {
297303

298304
checkm := gp.m
299305

300-
// Save current syscall parameters, so m.syscall can be
306+
// Save current syscall parameters, so m.winsyscall can be
301307
// used again if callback decide to make syscall.
302-
syscall := gp.m.syscall
308+
winsyscall := gp.m.winsyscall
303309

304310
// entersyscall saves the caller's SP to allow the GC to trace the Go
305311
// stack. However, since we're returning to an earlier stack frame and
@@ -340,7 +346,7 @@ func cgocallbackg(fn, frame unsafe.Pointer, ctxt uintptr) {
340346
// going back to cgo call
341347
reentersyscall(savedpc, uintptr(savedsp))
342348

343-
gp.m.syscall = syscall
349+
gp.m.winsyscall = winsyscall
344350
}
345351

346352
func cgocallbackg1(fn, frame unsafe.Pointer, ctxt uintptr) {

src/runtime/nonwindows_stub.go

+5
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,8 @@ func osRelax(relax bool) {}
2121
// enableWER is called by setTraceback("wer").
2222
// Windows Error Reporting (WER) is only supported on Windows.
2323
func enableWER() {}
24+
25+
// winlibcall is not implemented on non-Windows systems,
26+
// but it is used in non-OS-specific parts of the runtime.
27+
// Define it as an empty struct to avoid wasting stack space.
28+
type winlibcall struct{}

src/runtime/os_windows.go

+2
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,8 @@ func asmstdcall(fn unsafe.Pointer)
214214

215215
var asmstdcallAddr unsafe.Pointer
216216

217+
type winlibcall libcall
218+
217219
func windowsFindfunc(lib uintptr, name []byte) stdFunction {
218220
if name[len(name)-1] != 0 {
219221
throw("usage")

src/runtime/runtime2.go

+5-5
Original file line numberDiff line numberDiff line change
@@ -612,11 +612,11 @@ type m struct {
612612

613613
// these are here because they are too large to be on the stack
614614
// of low-level NOSPLIT functions.
615-
libcall libcall
616-
libcallpc uintptr // for cpu profiler
617-
libcallsp uintptr
618-
libcallg guintptr
619-
syscall libcall // stores syscall parameters on windows
615+
libcall libcall
616+
libcallpc uintptr // for cpu profiler
617+
libcallsp uintptr
618+
libcallg guintptr
619+
winsyscall winlibcall // stores syscall parameters on windows
620620

621621
vdsoSP uintptr // SP for traceback while in VDSO call (0 if not in call)
622622
vdsoPC uintptr // PC for traceback while in VDSO call

src/runtime/syscall_windows.go

+6-4
Original file line numberDiff line numberDiff line change
@@ -500,16 +500,18 @@ func syscall_SyscallN(fn uintptr, args ...uintptr) (r1, r2, err uintptr) {
500500
}
501501

502502
// The cgocall parameters are stored in m instead of in
503-
// the stack because the stack can move during if fn
503+
// the stack because the stack can move during fn if it
504504
// calls back into Go.
505-
lockOSThread()
506-
defer unlockOSThread()
507-
c := &getg().m.syscall
505+
c := &getg().m.winsyscall
508506
c.fn = fn
509507
c.n = uintptr(len(args))
510508
if c.n != 0 {
511509
c.args = uintptr(noescape(unsafe.Pointer(&args[0])))
512510
}
513511
cgocall(asmstdcallAddr, unsafe.Pointer(c))
512+
// cgocall may reschedule us on to a different M,
513+
// but it copies the return values into the new M's
514+
// so we can read them from there.
515+
c = &getg().m.winsyscall
514516
return c.r1, c.r2, c.err
515517
}

0 commit comments

Comments
 (0)