Golang面试中经常会遇到defer-panic-recover相关的题目,其底层工作原理是什么呢?
快速要点:
argp == uintptr(p.argp)
,要求recover时的栈指针和panic的栈指针要匹配,根据源码中的todo将来可能会改变这一要求// builtin.go#234
// The recover built-in function allows a program to manage behavior of a
// panicking goroutine. Executing a call to recover inside a deferred
// function (but not any function called by it) stops the panicking sequence
// by restoring normal execution and retrieves the error value passed to the
// call of panic. If recover is called outside the deferred function it will
// not stop a panicking sequence. In this case, or when the goroutine is not
// panicking, or if the argument supplied to panic was nil, recover returns
// nil. Thus the return value from recover reports whether the goroutine is
// panicking.
func recover() interface{}
// runtime/panic.go#1043, p is a panic
p.argp = unsafe.Pointer(getargp())
if goexperiment.RegabiDefer {
fn := deferFunc(d)
fn()
} else {
// Pass a dummy RegArgs since we'll only take this path if
// we're not using the register ABI.
var regs abi.RegArgs
reflectcall(nil, unsafe.Pointer(d.fn), deferArgs(d), uint32(d.siz), uint32(d.siz), uint32(d.siz), ®s)
}
}
// runtime/panic.go#1159
// The implementation of the predeclared function recover.
// Cannot split the stack because it needs to reliably
// find the stack segment of its caller.
//
// TODO(rsc): Once we commit to CopyStackAlways,
// this doesn't need to be nosplit.
//go:nosplit
func gorecover(argp uintptr) interface{} {
// Must be in a function running as part of a deferred call during the panic.
// Must be called from the topmost function of the call
// (the function used in the defer statement).
// p.argp is the argument pointer of that topmost deferred function call.
// Compare against argp reported by caller.
// If they match, the caller is the one who can recover.
gp := getg()
p := gp._panic
if p != nil && !p.goexit && !p.recovered && argp == uintptr(p.argp) {
p.recovered = true
return p.arg
}
return nil
}