gorecover函数如何获取其argp参数?

huangapple go评论71阅读模式
英文:

How does the gorecover funtion get its argp parameter?

问题

在Golang运行时源代码中,由嵌套的延迟函数调用的recover函数接收到的panic与当前goroutine的panic不同,这是因为在panic发生时,每个goroutine都会维护一个_panic结构体,其中包含了当前goroutine的panic信息。当调用recover函数时,它会检查当前goroutine的_panic结构体中的argp参数是否与传入的参数相匹配,如果匹配,则表示可以进行恢复。

gorecover函数是Golang运行时源代码中的一个函数,它用于实现recover函数的具体逻辑。argp参数是传入gorecover函数的参数,它表示当前goroutine的顶层延迟函数调用的参数指针。在gorecover函数中,通过比较argp参数和当前goroutine的_panic结构体中的argp字段,来确定是否可以进行恢复。

在你提供的示例代码中,当panic发生时,首先进入defer 1的延迟函数,然后进入defer 2的延迟函数。在defer 2的延迟函数中调用了recover函数。由于defer 2是在defer 1中定义的,所以它接收到的panic与当前goroutine的panic不同。

你在调试过程中获取到的_panic结构体的值是:

*runtime._panic 
{argp: unsafe.Pointer(0xc00007be58), 
arg: interface {}(int) 404, 
link: *runtime._panic nil, 
pc: 0, 
sp: unsafe.Pointer(0x0), 
recovered: false, 
aborted: false, 
goexit: false}

argp参数的值是:

(unreadable could not find loclist entry at 0x2ffec for address 0x1036d0a)

以上是关于Golang运行时实现细节的一些解释。

英文:

What is the logic in golang runtime source code that the recover called by a nested deferred function receives a different panic from the panic of the current goroutine?

and how does the gorecover funtion get its argp parameter?

This answer only tell me about the principle,
Why recover() does not work in a nested deferred function?

but I want to figure out the detail of the golang runtime implementation.

My demo code:

package main

import "fmt"

func main() {
	defer func() {
		fmt.Println("enter defer 1")
		defer func() {
			fmt.Println("enter defer 2")
			recover()
			// if e := recover(); e != nil {
			// 	fmt.Println("recover")
			// }
			fmt.Println("exit defer 2")
		}()
		fmt.Println("exit defer 1")
	}()
	panic(404)
}

My output

➜  cmd git:(master) ✗ go run main.go                              
enter defer 1
exit defer 1
enter defer 2
exit defer 2
panic: 404

goroutine 1 [running]:
main.main()
        /Users/polar/code/leetcode/leetcode-go/demo/defer-demo/cmd2/main.go:18 +0x49
exit status 2

I tried to debug to get more informations in this block of golang runtime source code:

the golang runtime source code about recover function.

// /Paht/to/go/src/runtime/panic.go
func gorecover(argp uintptr) any {
	// 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
}

Here are what I got:

The value of gp._panic is

*runtime._panic 
{argp: unsafe.Pointer(0xc00007be58), 
arg: interface {}(int) 404, 
link: *runtime._panic nil, 
pc: 0, 
sp: unsafe.Pointer(0x0), 
recovered: false, 
aborted: false, 
goexit: false}

The value of argp is

(unreadable could not find loclist entry at 0x2ffec for address 0x1036d0a)

答案1

得分: 1

从https://go.dev/ref/spec#Handling_panics

> 如果满足以下任何条件,则recover的返回值为nil:
>
> - panic的参数为nil;
> - 协程没有发生panic;
> - recover没有被一个延迟函数直接调用。

(强调是我的)

最后一条是你的情况。
只有"defer 1"是直接延迟的。你的"defer 2"不是为main函数延迟的(而是为defer 1延迟的,而该函数没有发生panic:在panic期间,它不在调用栈中被解开)。

(查看语言规范确实很有帮助:规范简短明了,通常很有帮助。)

英文:

From https://go.dev/ref/spec#Handling_panics

> The return value of recover is nil if any of the following conditions holds:
>
> - panic's argument was nil;
> - the goroutine is not panicking;
> - recover was not called directly by a deferred function.

(emph mine)

The last bullet point is your case.
Only the "defer 1" is directly defered. Your "defer 2" is not defered for main (but for defer 1 only and that function didn't panic: It's not on the call stack being unwound during panic).

(It really helps peeking at the language spec: The spec is short and clear and often helpful.)

huangapple
  • 本文由 发表于 2023年6月29日 00:27:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/76575086.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定