defer

defer

定义

type _defer struct {
    siz     int32 
    started bool
    sp      uintptr // sp at time of defer
    pc      uintptr
    fn      *funcval
    _panic  *_panic // panic that is running defer
    link    *_defer
}

相关字段解释:

  • sp 指向了栈指针
  • pc 指向了调用方的程序计数器
  • fn是向 defer 关键字中传入的函数
  • _panic是导致运行defer的panic
  • _ link是defer的链表,指向下一个defer结构

defer实现

func deferproc(siz int32, fn *funcval) { // arguments of fn follow fn
  sp := getcallersp()
  argp := uintptr(unsafe.Pointer(&fn)) + unsafe.Sizeof(fn)
  callerpc := getcallerpc()

  d := newdefer(siz)
  if d._panic != nil {
          throw("deferproc: d.panic != nil after newdefer")
  }
  d.fn = fn
  d.pc = callerpc
  d.sp = sp
  switch siz {
  case 0:
          // Do nothing.
  case sys.PtrSize:
          *(*uintptr)(deferArgs(d)) = *(*uintptr)(unsafe.Pointer(argp))
  default:
          memmove(deferArgs(d), unsafe.Pointer(argp), uintptr(siz))
  }
  return0()
}

func newdefer(siz int32) *_defer {
    var d *_defer
    sc := deferclass(uintptr(siz))
    gp := getg()
    if sc < uintptr(len(p{}.deferpool)) {
            pp := gp.m.p.ptr()
            if len(pp.deferpool[sc]) == 0 && sched.deferpool[sc] != nil {
                    // Take the slow path on the system stack so
                    // we don't grow newdefer's stack.
                    systemstack(func() {
                            lock(&sched.deferlock)
                            for len(pp.deferpool[sc]) < cap(pp.deferpool[sc])/2 && sched.deferpool[sc] != nil {
                                    d := sched.deferpool[sc]
                                    sched.deferpool[sc] = d.link
                                    d.link = nil
                                    pp.deferpool[sc] = append(pp.deferpool[sc], d)
                            }
                            unlock(&sched.deferlock)
                    })
            }
            if n := len(pp.deferpool[sc]); n > 0 {
                    d = pp.deferpool[sc][n-1]
                    pp.deferpool[sc][n-1] = nil
                    pp.deferpool[sc] = pp.deferpool[sc][:n-1]
            }
    }
    if d == nil {
            // Allocate new defer+args.
            systemstack(func() {
                    total := roundupsize(totaldefersize(uintptr(siz)))
                    d = (*_defer)(mallocgc(total, deferType, true))
            })
            if debugCachedWork {
                    // Duplicate the tail below so if there's a
                    // crash in checkPut we can tell if d was just
                    // allocated or came from the pool.
                    d.siz = siz
                    d.link = gp._defer
                    gp._defer = d
                    return d
            }
    }
    d.siz = siz
    d.link = gp._defer
    gp._defer = d //用链表连接当前g的所有defer,头插法,所以defer调用的顺序是先入后出的
    return d
}
  • 每遇到一个defer关键字,defer函数都会被转换成runtime.deferproc

  • deferproc通过newdefer创建一个延迟函数,并将这个新建的延迟函数挂在当前goroutine的_defer的链表上

  • newdefer会先从sched和当前p的deferpool取出一个_defer结构体,如果deferpool没有_defer,则初始化一个新的_defer

  • _defer是关联到当前的G,所以defer只对当前G有效。

  • deferreturn从当前G取出_defer链表执行,每个_defer调用freedefer释放_defer结构体,并将该_defer结构体放入当前P的deferpool中。

参考

原文地址:https://www.cnblogs.com/weiweng/p/13282735.html