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){
	call()
	fmt.Println("333 Helloworld")}func call(a)  {
	
	defer func(a){
		fmt.Println("11111")
	}()
	defer func(a){
		fmt.Println("22222")
	}()
	defer func(a) {
		if r := recover(a); r! =nil {
			fmt.Println("Recover from r : ",r)
		}
	}()
	defer func(a){
		fmt.Println("33333")
	}()

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

Output result:

111 Helloworld
33333
Recover from r :  Panic 1!
22222
11111
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: ")
        printany(e)
        print("\n")
        throw("panic on system stack")}// omit useless code.
    var p _panic
    p.arg = e
    p.link = 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 = d.link
            freedefer(d)  // Defer has already been executed, then release the defer and continue the for loop.
            continue
        }

        // 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 = d.link  // Remove the just-executed defer from the defer chain

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

        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 = p.link
            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.
    preprintpanics(gp._panic)
    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

Conclusion:

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 ~