mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
runtime: don't call lockOSThread for every cgo call
For a trivial benchmark with a do-nothing cgo call: name old time/op new time/op delta Call-4 64.5ns ± 7% 63.0ns ± 6% -2.25% (p=0.027 n=20+16) Because Windows uses the cgocall mechanism to make system calls, and passes arguments in a struct held in the m, we need to do the lockOSThread/unlockOSThread in that code. Because deferreturn was getting a nosplit stack overflow error, change it to avoid calling typedmemmove. Updates #21827. Change-Id: I9b1d61434c44faeb29805b46b409c812c9acadc2 Reviewed-on: https://go-review.googlesource.com/64070 Run-TryBot: Ian Lance Taylor <iant@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Austin Clements <austin@google.com> Reviewed-by: David Crawshaw <crawshaw@golang.org>
This commit is contained in:
parent
9daee93121
commit
332719f7ce
5 changed files with 83 additions and 36 deletions
|
|
@ -8,9 +8,9 @@
|
|||
// runtime.cgocall(_cgo_Cfunc_f, frame), where _cgo_Cfunc_f is a
|
||||
// gcc-compiled function written by cgo.
|
||||
//
|
||||
// runtime.cgocall (below) locks g to m, calls entersyscall
|
||||
// so as not to block other goroutines or the garbage collector,
|
||||
// and then calls runtime.asmcgocall(_cgo_Cfunc_f, frame).
|
||||
// runtime.cgocall (below) calls entersyscall so as not to block
|
||||
// other goroutines or the garbage collector, and then calls
|
||||
// runtime.asmcgocall(_cgo_Cfunc_f, frame).
|
||||
//
|
||||
// runtime.asmcgocall (in asm_$GOARCH.s) switches to the m->g0 stack
|
||||
// (assumed to be an operating system-allocated stack, so safe to run
|
||||
|
|
@ -104,13 +104,9 @@ func cgocall(fn, arg unsafe.Pointer) int32 {
|
|||
racereleasemerge(unsafe.Pointer(&racecgosync))
|
||||
}
|
||||
|
||||
// Lock g to m to ensure we stay on the same stack if we do a
|
||||
// cgo callback. In case of panic, unwindm calls endcgo.
|
||||
lockOSThread()
|
||||
mp := getg().m
|
||||
mp.ncgocall++
|
||||
mp.ncgo++
|
||||
mp.incgo = true
|
||||
|
||||
// Reset traceback.
|
||||
mp.cgoCallers[0] = 0
|
||||
|
|
@ -130,7 +126,14 @@ func cgocall(fn, arg unsafe.Pointer) int32 {
|
|||
// and then re-enter the "system call" reusing the PC and SP
|
||||
// saved by entersyscall here.
|
||||
entersyscall(0)
|
||||
|
||||
mp.incgo = true
|
||||
errno := asmcgocall(fn, arg)
|
||||
|
||||
// Call endcgo before exitsyscall because exitsyscall may
|
||||
// reschedule us on to a different M.
|
||||
endcgo(mp)
|
||||
|
||||
exitsyscall(0)
|
||||
|
||||
// From the garbage collector's perspective, time can move
|
||||
|
|
@ -145,8 +148,8 @@ func cgocall(fn, arg unsafe.Pointer) int32 {
|
|||
// GC by forcing them to stay live across this time warp.
|
||||
KeepAlive(fn)
|
||||
KeepAlive(arg)
|
||||
KeepAlive(mp)
|
||||
|
||||
endcgo(mp)
|
||||
return errno
|
||||
}
|
||||
|
||||
|
|
@ -158,8 +161,6 @@ func endcgo(mp *m) {
|
|||
if raceenabled {
|
||||
raceacquire(unsafe.Pointer(&racecgosync))
|
||||
}
|
||||
|
||||
unlockOSThread() // invalidates mp
|
||||
}
|
||||
|
||||
// Call from C back to Go.
|
||||
|
|
@ -171,6 +172,12 @@ func cgocallbackg(ctxt uintptr) {
|
|||
exit(2)
|
||||
}
|
||||
|
||||
// The call from C is on gp.m's g0 stack, so we must ensure
|
||||
// that we stay on that M. We have to do this before calling
|
||||
// exitsyscall, since it would otherwise be free to move us to
|
||||
// a different M. The call to unlockOSThread is in unwindm.
|
||||
lockOSThread()
|
||||
|
||||
// Save current syscall parameters, so m.syscall can be
|
||||
// used again if callback decide to make syscall.
|
||||
syscall := gp.m.syscall
|
||||
|
|
@ -186,6 +193,10 @@ func cgocallbackg(ctxt uintptr) {
|
|||
|
||||
cgocallbackg1(ctxt)
|
||||
|
||||
// At this point unlockOSThread has been called.
|
||||
// The following code must not change to a different m.
|
||||
// This is enforced by checking incgo in the schedule function.
|
||||
|
||||
gp.m.incgo = true
|
||||
// going back to cgo call
|
||||
reentersyscall(savedpc, uintptr(savedsp))
|
||||
|
|
@ -321,9 +332,7 @@ func cgocallbackg1(ctxt uintptr) {
|
|||
}
|
||||
|
||||
func unwindm(restore *bool) {
|
||||
if !*restore {
|
||||
return
|
||||
}
|
||||
if *restore {
|
||||
// Restore sp saved by cgocallback during
|
||||
// unwind of g's stack (see comment at top of file).
|
||||
mp := acquirem()
|
||||
|
|
@ -349,6 +358,11 @@ func unwindm(restore *bool) {
|
|||
releasem(mp)
|
||||
}
|
||||
|
||||
// Undo the call to lockOSThread in cgocallbackg.
|
||||
// We must still stay on the same m.
|
||||
unlockOSThread()
|
||||
}
|
||||
|
||||
// called from assembly
|
||||
func badcgocallback() {
|
||||
throw("misaligned stack in cgocallback")
|
||||
|
|
|
|||
|
|
@ -273,7 +273,17 @@ func freedefer(d *_defer) {
|
|||
unlock(&sched.deferlock)
|
||||
})
|
||||
}
|
||||
*d = _defer{}
|
||||
|
||||
// These lines used to be simply `*d = _defer{}` but that
|
||||
// started causing a nosplit stack overflow via typedmemmove.
|
||||
d.siz = 0
|
||||
d.started = false
|
||||
d.sp = 0
|
||||
d.pc = 0
|
||||
d.fn = nil
|
||||
d._panic = nil
|
||||
d.link = nil
|
||||
|
||||
pp.deferpool[sc] = append(pp.deferpool[sc], d)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2221,6 +2221,12 @@ func schedule() {
|
|||
execute(_g_.m.lockedg.ptr(), false) // Never returns.
|
||||
}
|
||||
|
||||
// We should not schedule away from a g that is executing a cgo call,
|
||||
// since the cgo call is using the m's g0 stack.
|
||||
if _g_.m.incgo {
|
||||
throw("schedule: in cgo")
|
||||
}
|
||||
|
||||
top:
|
||||
if sched.gcwaiting != 0 {
|
||||
gcstopm()
|
||||
|
|
|
|||
|
|
@ -675,7 +675,8 @@ func extendRandom(r []byte, n int) {
|
|||
}
|
||||
}
|
||||
|
||||
// deferred subroutine calls
|
||||
// A _defer holds an entry on the list of deferred calls.
|
||||
// If you add a field here, add code to clear it in freedefer.
|
||||
type _defer struct {
|
||||
siz int32
|
||||
started bool
|
||||
|
|
|
|||
|
|
@ -93,6 +93,8 @@ const _LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800
|
|||
//go:linkname syscall_loadsystemlibrary syscall.loadsystemlibrary
|
||||
//go:nosplit
|
||||
func syscall_loadsystemlibrary(filename *uint16) (handle, err uintptr) {
|
||||
lockOSThread()
|
||||
defer unlockOSThread()
|
||||
c := &getg().m.syscall
|
||||
|
||||
if useLoadLibraryEx {
|
||||
|
|
@ -126,6 +128,8 @@ func syscall_loadsystemlibrary(filename *uint16) (handle, err uintptr) {
|
|||
//go:linkname syscall_loadlibrary syscall.loadlibrary
|
||||
//go:nosplit
|
||||
func syscall_loadlibrary(filename *uint16) (handle, err uintptr) {
|
||||
lockOSThread()
|
||||
defer unlockOSThread()
|
||||
c := &getg().m.syscall
|
||||
c.fn = getLoadLibrary()
|
||||
c.n = 1
|
||||
|
|
@ -141,6 +145,8 @@ func syscall_loadlibrary(filename *uint16) (handle, err uintptr) {
|
|||
//go:linkname syscall_getprocaddress syscall.getprocaddress
|
||||
//go:nosplit
|
||||
func syscall_getprocaddress(handle uintptr, procname *byte) (outhandle, err uintptr) {
|
||||
lockOSThread()
|
||||
defer unlockOSThread()
|
||||
c := &getg().m.syscall
|
||||
c.fn = getGetProcAddress()
|
||||
c.n = 2
|
||||
|
|
@ -156,6 +162,8 @@ func syscall_getprocaddress(handle uintptr, procname *byte) (outhandle, err uint
|
|||
//go:linkname syscall_Syscall syscall.Syscall
|
||||
//go:nosplit
|
||||
func syscall_Syscall(fn, nargs, a1, a2, a3 uintptr) (r1, r2, err uintptr) {
|
||||
lockOSThread()
|
||||
defer unlockOSThread()
|
||||
c := &getg().m.syscall
|
||||
c.fn = fn
|
||||
c.n = nargs
|
||||
|
|
@ -167,6 +175,8 @@ func syscall_Syscall(fn, nargs, a1, a2, a3 uintptr) (r1, r2, err uintptr) {
|
|||
//go:linkname syscall_Syscall6 syscall.Syscall6
|
||||
//go:nosplit
|
||||
func syscall_Syscall6(fn, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) {
|
||||
lockOSThread()
|
||||
defer unlockOSThread()
|
||||
c := &getg().m.syscall
|
||||
c.fn = fn
|
||||
c.n = nargs
|
||||
|
|
@ -178,6 +188,8 @@ func syscall_Syscall6(fn, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err ui
|
|||
//go:linkname syscall_Syscall9 syscall.Syscall9
|
||||
//go:nosplit
|
||||
func syscall_Syscall9(fn, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2, err uintptr) {
|
||||
lockOSThread()
|
||||
defer unlockOSThread()
|
||||
c := &getg().m.syscall
|
||||
c.fn = fn
|
||||
c.n = nargs
|
||||
|
|
@ -189,6 +201,8 @@ func syscall_Syscall9(fn, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1
|
|||
//go:linkname syscall_Syscall12 syscall.Syscall12
|
||||
//go:nosplit
|
||||
func syscall_Syscall12(fn, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12 uintptr) (r1, r2, err uintptr) {
|
||||
lockOSThread()
|
||||
defer unlockOSThread()
|
||||
c := &getg().m.syscall
|
||||
c.fn = fn
|
||||
c.n = nargs
|
||||
|
|
@ -200,6 +214,8 @@ func syscall_Syscall12(fn, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11,
|
|||
//go:linkname syscall_Syscall15 syscall.Syscall15
|
||||
//go:nosplit
|
||||
func syscall_Syscall15(fn, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 uintptr) (r1, r2, err uintptr) {
|
||||
lockOSThread()
|
||||
defer unlockOSThread()
|
||||
c := &getg().m.syscall
|
||||
c.fn = fn
|
||||
c.n = nargs
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue