The usage of Go exception handling

The exception handling usage in Go is defer… recover… Combination of panic

Panic is used to throw exceptions, and RECOVER is used to intercept unprocessed Panic. Panic will cause the function to exit, and the function defined by defer will be executed before the function is pushed out, resulting in Panic being intercepted by recover

package main

import "log"

func main(a) {
	defer func(a) {
		if err := recover(a); err ! =nil {
			log.Println(err)
		}
	}()
	test()
}

func test(a){
	panic("error")}Copy the code

The implementation principle of Panic

Panic is compiled for runtime.gopanic with the following code:

// Gopanic accepts a single parameter that is passed through the panic function
func gopanic(e interface{}) {
        
	gp := getg()
	// Get g and make some predictable misjudgments about g
        // ...
        
        /// Create a _panic
	var p _panic
	p.arg = e
	p.link = gp._panic
        // Place panic at the head of the _panic list in g
	gp._panic = (*_panic)(noescape(unsafe.Pointer(&p)))
       
        // When panic occurs, the _defer queue for current G is processed immediately
	for {
		d := gp._defer
                // Jump out if defer does not exist
		if d == nil {
			break
		}

		// Determine whether to defer if there is one
		if d.started {
			ifd._panic ! =nil {
				d._panic.aborted = true
			}
			d._panic = nil
			if! d.openDefer {// For open-coded defers, we need to process the
				// defer again, in case there are any other defers
				// to call in the frame (not including the defer
				// call that caused the panic).
				d.fn = nil
				gp._defer = d.link
				freedefer(d)
				continue}}// mark the start state
		d.started = true

		// Copy the panic in G to the current defer. As you can see from the code above, Panic sets the current panic for each defer
		d._panic = (*_panic)(noescape(unsafe.Pointer(&p)))
                
		done := true
                
                // Will the function defined by defer be called anyway
		if d.openDefer {
			done = runOpenDeferFrame(gp, d)
			ifdone && ! d._panic.recovered { addOneOpenDeferFrame(gp,0.nil)}}else {
			p.argp = unsafe.Pointer(getargp(0))
			reflectcall(nil, unsafe.Pointer(d.fn), deferArgs(d), uint32(d.siz), uint32(d.siz))
		}
                // Once the call is complete, the following code clears references to current defer
		p.argp = nil

		// reflectcall did not panic. Remove d.
		ifgp._defer ! = d { throw("bad defer entry in panic")
		}
		d._panic = nil
                // Point _deger in GP to the next deger object on the _defer linked list
		if done {
			d.fn = nil
			gp._defer = d.link
			freedefer(d)
		}
                // Check whether panic is handled. If so, destroy panic
		if p.recovered {
			Exit the gopanic function and return to the normal function call
			mcall(recovery)
                        // If McAll returns here, there is an error, throw throws a fatal error that causes exit() to be called
			throw("recovery failed") // mcall should not return}}// Prints error stack information
	preprintpanics(gp._panic)
        // Fatal error, call exit() to exit
	fatalpanic(gp._panic) // should not return* (*int) (nil) = 0      // not reached
}
Copy the code
  1. The compiler compiles Panic to runtime.gopanic
  2. In the Gopanic function, the corresponding function in the defer chain is called
  3. If recover is present to handle the current panic, dia uses the McAll function to return to the normal function call
  4. If panic is not processed after all the defer chains have been executed, call the Fatalpanic function
  5. Fatalpanic causes the program to exit because it calls exit internally

From the analysis of the above code, at least the following conclusions can be drawn

  1. Panic triggers the defer chain to be called
  2. The defer chain is always fully invoked