162 lines
4.4 KiB
Go
162 lines
4.4 KiB
Go
// Copyright 2016 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package runtime
|
|
|
|
import (
|
|
"runtime/internal/atomic"
|
|
"unsafe"
|
|
)
|
|
|
|
// For historical reasons these functions are called as though they
|
|
// were in the syscall package.
|
|
//go:linkname Cgocall syscall.Cgocall
|
|
//go:linkname CgocallDone syscall.CgocallDone
|
|
//go:linkname CgocallBack syscall.CgocallBack
|
|
//go:linkname CgocallBackDone syscall.CgocallBackDone
|
|
|
|
// A routine that may be called by SWIG.
|
|
//go:linkname _cgo_panic _cgo_panic
|
|
|
|
// iscgo is set to true if the cgo tool sets the C variable runtime_iscgo
|
|
// to true.
|
|
var iscgo bool
|
|
|
|
// cgoHasExtraM is set on startup when an extra M is created for cgo.
|
|
// The extra M must be created before any C/C++ code calls cgocallback.
|
|
var cgoHasExtraM bool
|
|
|
|
// cgoAlwaysFalse is a boolean value that is always false.
|
|
// The cgo-generated code says if cgoAlwaysFalse { cgoUse(p) }.
|
|
// The compiler cannot see that cgoAlwaysFalse is always false,
|
|
// so it emits the test and keeps the call, giving the desired
|
|
// escape analysis result. The test is cheaper than the call.
|
|
var cgoAlwaysFalse bool
|
|
|
|
// Cgocall prepares to call from code written in Go to code written in
|
|
// C/C++. This takes the current goroutine out of the Go scheduler, as
|
|
// though it were making a system call. Otherwise the program can
|
|
// lookup if the C code blocks. The idea is to call this function,
|
|
// then immediately call the C/C++ function. After the C/C++ function
|
|
// returns, call cgocalldone. The usual Go code would look like
|
|
// syscall.Cgocall()
|
|
// defer syscall.Cgocalldone()
|
|
// cfunction()
|
|
func Cgocall() {
|
|
mp := getg().m
|
|
mp.ncgocall++
|
|
mp.ncgo++
|
|
entersyscall()
|
|
mp.incgo = true
|
|
}
|
|
|
|
// CgocallDone prepares to return to Go code from C/C++ code.
|
|
func CgocallDone() {
|
|
gp := getg()
|
|
if gp == nil {
|
|
throw("no g in CgocallDone")
|
|
}
|
|
gp.m.incgo = false
|
|
gp.m.ncgo--
|
|
|
|
// If we are invoked because the C function called _cgo_panic,
|
|
// then _cgo_panic will already have exited syscall mode.
|
|
if readgstatus(gp)&^_Gscan == _Gsyscall {
|
|
exitsyscall()
|
|
}
|
|
}
|
|
|
|
// CgocallBack is used when calling from C/C++ code into Go code.
|
|
// The usual approach is
|
|
// syscall.CgocallBack()
|
|
// defer syscall.CgocallBackDone()
|
|
// gofunction()
|
|
//go:nosplit
|
|
func CgocallBack() {
|
|
gp := getg()
|
|
if gp == nil || gp.m == nil {
|
|
needm()
|
|
gp = getg()
|
|
mp := gp.m
|
|
mp.dropextram = true
|
|
|
|
// This is a C-created stack.
|
|
// Record the outermost Go frame to help stack scan.
|
|
gp.entrysp = getcallersp()
|
|
}
|
|
|
|
lockOSThread()
|
|
|
|
gp.m.incgo = false
|
|
exitsyscall()
|
|
|
|
if gp.m.ncgo == 0 {
|
|
// The C call to Go came from a thread created by C.
|
|
// The C call to Go came from a thread not currently running
|
|
// any Go. In the case of -buildmode=c-archive or c-shared,
|
|
// this call may be coming in before package initialization
|
|
// is complete. Wait until it is.
|
|
<-main_init_done
|
|
}
|
|
|
|
mp := gp.m
|
|
if mp.needextram || atomic.Load(&extraMWaiters) > 0 {
|
|
mp.needextram = false
|
|
newextram()
|
|
}
|
|
}
|
|
|
|
// CgocallBackDone prepares to return to C/C++ code that has called
|
|
// into Go code.
|
|
func CgocallBackDone() {
|
|
unlockOSThread()
|
|
|
|
// We are going to stop running in Go mode and return to C mode.
|
|
// We were almost certainly called by defer; if so, clean up
|
|
// the defer struct now, before we leave Go mode. But don't
|
|
// leave Go mode if we are panicing or called from Goexit,
|
|
// since in those cases we will continue executing deferred functions.
|
|
gp := getg()
|
|
mp := gp.m
|
|
drop := false
|
|
if gp.deferring && gp._panic == nil && !gp.goexiting {
|
|
d := gp._defer
|
|
if d == nil {
|
|
throw("no defer struct when deferring")
|
|
}
|
|
gp._defer = d.link
|
|
freedefer(d)
|
|
|
|
// If we are the top level Go function called from C,
|
|
// then we need to release the m.
|
|
if mp.dropextram && mp.ncgo == 0 {
|
|
drop = true
|
|
}
|
|
}
|
|
|
|
// Don't go back to C mode if we are panicing. Just let the
|
|
// panic walk up through the Go stack.
|
|
if gp._panic == nil && !gp.goexiting {
|
|
gp.m.incgo = true
|
|
entersyscall()
|
|
}
|
|
|
|
if drop {
|
|
mp.dropextram = false
|
|
dropm()
|
|
} else if gp.deferring && gp._panic == nil && !gp.goexiting {
|
|
gp.ranCgocallBackDone = true
|
|
}
|
|
}
|
|
|
|
// _cgo_panic may be called by SWIG code to panic.
|
|
func _cgo_panic(p *byte) {
|
|
exitsyscall()
|
|
panic(gostringnocopy(p))
|
|
}
|
|
|
|
// cgo_yield exists in the gc toolchain to let TSAN deliver a signal.
|
|
// gccgo does not need this.
|
|
var cgo_yield = &_cgo_yield
|
|
var _cgo_yield unsafe.Pointer
|