// Create a new deferred function fn with siz bytes of arguments. // The compiler turns a defer statement into a call to this. //go:nosplit //siz表示fn函数的参数总大小 funcdeferproc(siz int32, fn *funcval) { // arguments of fn follow fn //deferproc不允许在系统栈执行 if getg().m.curg != getg() { // go code on the system stack can't defer throw("defer on system stack") }
// the arguments of fn are in a perilous state. The stack map // for deferproc does not describe them. So we can't let garbage // collection or stack copying trigger until we've copied them out // to somewhere safe. The memmove below does that. // Until the copy completes, we can only call nosplit routines. sp := getcallersp(unsafe.Pointer(&siz)) //fn的参数紧跟在fn之后,因此通过简单的指针运算可以获取fn的参数起始地址 argp := uintptr(unsafe.Pointer(&fn)) + unsafe.Sizeof(fn) // 获取当前函数的调用者的PC callerpc := getcallerpc() //获取一个_defer d := newdefer(siz) if d._panic != nil { throw("deferproc: d.panic != nil after newdefer") } d.fn = fn d.pc = callerpc // 保存当前的SP d.sp = sp switch siz { case0: // Do nothing. case sys.PtrSize: *(*uintptr)(deferArgs(d)) = *(*uintptr)(unsafe.Pointer(argp)) default: //deferArgs:分配_defer时,连同参数存储空间一起分配,参数紧跟_defer之后存储,该函数进行指针运算,返回参数的起始地址: //拷贝参数,因此在执行defer语句语义之前,需要先准备好接收者和参数 memmove(deferArgs(d), unsafe.Pointer(argp), uintptr(siz)) }
// deferproc returns 0 normally. // a deferred func that stops a panic // makes the deferproc return 1. // the code the compiler generates always // checks the return value and jumps to the // end of the function if deferproc returns != 0. return0() // No code can go here - the C return register has // been set and must not be clobbered. }
// A _defer holds an entry on the list of deferred calls. // If you add a field here, add code to clear it in freedefer. type _defer struct { siz int32//参数size started bool//是否执行过 sp uintptr// sp at time of defer pc uintptr fn *funcval //需要延时执行的函数地址 _panic *_panic // panic that is running defer link *_defer //每个goroutine中的_defer以链表组织 }
// Run a deferred function if there is one. // The compiler inserts a call to this at the end of any // function which calls defer. // If there is a deferred function, this will call runtime·jmpdefer, // which will jump to the deferred function such that it appears // to have been called by the caller of deferreturn at the point // just before deferreturn was called. The effect is that deferreturn // is called again and again until there are no more deferred functions. // Cannot split the stack because we reuse the caller's frame to // call the deferred function.
// The single argument isn't actually used - it just has its address // taken so it can be matched against pending defers. //go:nosplit funcdeferreturn(arg0 uintptr) { //这边的arg0只是为了获取当前的sp gp := getg() d := gp._defer //获取_defer链表头部 //如果没有_defer,则返回,详见上面注释 if d == nil { return } // 当前goroutine的所有的_defer通过链表连接 // 这里通过比较SP,确保只执行当前函数的_defer sp := getcallersp(unsafe.Pointer(&arg0)) if d.sp != sp { return }
// Moving arguments around. // // Everything called after this point must be recursively // nosplit because the garbage collector won't know the form // of the arguments until the jmpdefer can flip the PC over to // fn. //拷贝参数到sp中 switch d.siz { case0: // Do nothing. case sys.PtrSize: *(*uintptr)(unsafe.Pointer(&arg0)) = *(*uintptr)(deferArgs(d)) default: memmove(unsafe.Pointer(&arg0), deferArgs(d), uintptr(d.siz)) } fn := d.fn d.fn = nil gp._defer = d.link //从链表中移除 freedefer(d) //释放当前_defer //call runtime·jmpdefer, // which will jump to the deferred function such that it appears // to have been called by the caller of deferreturn at the point // just before deferreturn was called. The effect is that deferreturn // is called again and again until there are no more deferred fns. //执行fn,并修改pc为 `CALL runtime.deferreturn(SB)`,下一条指令再次进入该函数,如果gp.defer为nil或者sp不一致,则返回,否则继续执行defer //每次添加defer时,总是添加到head,处理时则是从head开始处理,因此defer的处理顺序是FILO jmpdefer(fn, uintptr(unsafe.Pointer(&arg0))) }
// 内置函数panic的实现 funcgopanic(e interface{}) { gp := getg() // 当前panic的g // 在系统栈panic if gp.m.curg != gp { print("panic: ") printany(e) print("\n") throw("panic on system stack") // throw是不可恢复的,直接终止进程 } // 在内存分配过程中panic if gp.m.mallocing != 0 { print("panic: ") printany(e) print("\n") throw("panic during malloc") } if gp.m.preemptoff != "" { print("panic: ") printany(e) print("\n") print("preempt off reason: ") print(gp.m.preemptoff) print("\n") throw("panic during preemptoff") } if gp.m.locks != 0 { print("panic: ") printany(e) print("\n") throw("panic holding locks") }
var p _panic p.arg = e p.link = gp._panic // 在defer中可以通过recover获取到该_panic gp._panic = (*_panic)(noescape(unsafe.Pointer(&p))) // 统计 atomic.Xadd(&runningPanicDefers, 1)
// 依次执行当前goroutine的_defer for { d := gp._defer if d == nil { break }
// 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. // defer已经开始执行了,执行defer的时候又触发了panic if d.started { // 如果存在早期的panic if d._panic != nil { // 终止原来的panic d._panic.aborted = true } d._panic = nil d.fn = nil gp._defer = d.link freedefer(d) // 继续下一个defer 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. // 设置defer d._panic = (*_panic)(noescape(unsafe.Pointer(&p)))
// reflectcall did not panic. Remove d. if gp._defer != d { throw("bad defer entry in panic") } d._panic = nil d.fn = nil gp._defer = d.link
// 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) // 如果在defer中recover了 if p.recovered { atomic.Xadd(&runningPanicDefers, -1)
gp._panic = p.link // Aborted panics are marked but remain on the g.panic list. // Remove them from the list. // 移除已经aborted的panic for gp._panic != nil && gp._panic.aborted { gp._panic = gp._panic.link } if gp._panic == nil { // must be done with signal gp.sig = 0 } // Pass information about recovering frame to recovery. gp.sigcode0 = uintptr(sp) gp.sigcode1 = pc // 调用recovery,恢复执行 mcall(recovery) 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)
fatalpanic(gp._panic) // should not return *(*int)(nil) = 0// not reached }