Moment For Technology

Defer and Panic are executed in go

Posted on Jan. 31, 2023, 12:57 p.m. by Pamela Howell
Category: The back-end Tag: Go

The execution order of defer and Panic in ?? go.

This article focuses on the relationship between defer and panic execution order, rather than analyzing the source code for panpanic. Go.

? can start with the Demo below.
func main(a){
	fmt.Println("333 Helloworld")}func call(a)  {
	defer func(a){
	defer func(a){
	defer func(a) {
		if r := recover(a); r! =nil {
			fmt.Println("Recover from r : ",r)
	defer func(a){

	fmt.Println("111 Helloworld")
	panic("Panic 1!")
	panic("Panic 2!")
	fmt.Println("222 Helloworld")}Copy the code

Output result:

111 Helloworld
Recover from r :  Panic 1!
333 Helloworld
Copy the code
? 为什么?

We from the Panic source code analysis, Panic source is located in the % GOROOT % / SRC/rumtime/Panic. Go: 425

I don't know much about Gopanic, but just to get a sense of how it works. A lot of judgment information irrelevant to this article was deleted in the middle.

// Panic keyword code implementation.
func gopanic(e interface{}) {
    gp := getg()    // getg() returns the pointer to the g structure of the current coroutine, which describes goroutine
    ifgp.m.curg ! = gp {print("panic: ")
        throw("panic on system stack")}// omit useless code.
    var p _panic
    p.arg = e = gp._panic
    gp._panic = (*_panic)(noescape(unsafe.Pointer(p)))

    atomic.Xadd(runningPanicDefers, 1)

    for {
        // Get the current coroutine defer linked list.
        // Here defer's linked list is the opposite of the order defined in the code, similar to the concept of first in, last out.
        d := gp._defer    
        if d == nil {
            break    // After all of the current defer coroutines have been executed, the deferred linked list is empty and the for loop exits

        // If defer was started by earlier panic or Goexit (and, since we're back here, that triggered a new panic),
        // take defer off list. The earlier panic or Goexit will not continue running.
        if d.started {    // After a panic occurs, panic() is encountered in defer, and this code block is entered
            ifd._panic ! =nil {
                d._panic.aborted = true
            d._panic = nil
            d.fn = nil
            gp._defer =
            freedefer(d)  // Defer has already been executed, then release the defer and continue the for loop.

        // Mark defer as started, but keep on list, so that traceback
        // can find and update the defer's argument frame if stack growth
        // or a garbage collection happens before reflectcall starts executing d.fn.
        d.started = true

        // Record the panic that is running the defer.
        // If there is a new panic during the deferred call, that panic
        // will find d in the list and will mark d._panic (this panic) aborted.
        d._panic = (*_panic)(noescape(unsafe.Pointer(p)))

        p.argp = unsafe.Pointer(getargp(0))
        reflectcall(nil, unsafe.Pointer(d.fn), deferArgs(d), uint32(d.siz), uint32(d.siz))   // Execute the current coroutine defer defer header
        p.argp = nil

        // reflectcall did not panic. Remove d.
        ifgp._defer ! = d { throw("bad defer entry in panic")
        d._panic = nil
        d.fn = nil
        gp._defer =  // Remove the just-executed defer from the defer chain

        // trigger shrinkage to test stack copy. See stack_test.go:TestStackPanic

        pc := d.pc
        sp := unsafe.Pointer(d.sp) // must be pointer so it gets adjusted during stack copy
        freedefer(d)   // Release the defer that you just performed
        if p.recovered {    // defer() encounters recover and enters the code block
            atomic.Xadd(runningPanicDefers, - 1)

            gp._panic =
            mcall(recovery)   // Jump to recover() and continue
            throw("recovery failed") // mcall should not return}}// ran out of deferred calls - old-school panic now
    // Because it is unsafe to call arbitrary user code after freezing
    // the world, we call preprintpanics to invoke all necessary Error
    // and String methods to prepare the panic strings before startpanic.

    // startpanic set panicking, which will block main from exiting,
    // so now OK to decrement runningPanicDefers.
    atomic.Xadd(runningPanicDefers, - 1)
    printpanics(gp._panic)   // Displays panic information
    dopanic(0)       // should not return* (*int) (nil) = 0 // not reached
Copy the code


The general flow is that if the panic keyword is encountered, the go execuator will enter code gopanic and get a pointer to the current coroutine G. It will then use this pointer to get the current coroutine's deferred linked list, and execute defer through the for loop. If panic is encountered in defer, it will be released and the next one will be executed with a continue. Then the next one will be executed one by one. If recover is encountered in defer, Then panic will be executed using McAll (recovery). So, you should now understand what the order of panic and defer is?

If it is helpful to you, please pay attention to my public number ~

About (Moment For Technology) is a global community with thousands techies from across the global hang out!Passionate technologists, be it gadget freaks, tech enthusiasts, coders, technopreneurs, or CIOs, you would find them all here.