// doCall handles the single call for a key. func(g *Group) doCall(c *call, key string, fn func() (interface{}, error)) { // 判断是否正常退出fn函数 normalReturn := false // 判断函数中是否panic recovered := false
// use double-defer to distinguish panic from runtime.Goexit, // more details see https://golang.org/cl/134395 // 两个defer去判断是fn内部panic还是runtime.Goexit deferfunc() { // the given function invoked runtime.Goexit // fn没有正常退出,且recovered仍为false // 说明没走到最下面的 if !normalReturn 判断,认为发生了runtime.Goexit if !normalReturn && !recovered { c.err = errGoexit }
g.mu.Lock() defer g.mu.Unlock() c.wg.Done() // 删除该key,下次执行时遇到该key重新调fn去 if g.m[key] == c { delete(g.m, key) }
if e, ok := c.err.(*panicError); ok { // In order to prevent the waiting channels from being blocked forever, // needs to ensure that this panic cannot be recovered. iflen(c.chans) > 0 { gopanic(e) select {} // Keep this goroutine around so that it will appear in the crash dump. } else { panic(e) } } elseif c.err == errGoexit { // Already in the process of goexit, no need to call again } else { // Normal return // 写到ch中,DoChan方法调用的话会用到 for _, ch := range c.chans { ch <- Result{c.val, c.err, c.dups > 0} } } }()
func() { deferfunc() { if !normalReturn { // Ideally, we would wait to take a stack trace until we've determined // whether this is a panic or a runtime.Goexit. // // Unfortunately, the only way we can distinguish the two is to see // whether the recover stopped the goroutine from terminating, and by // the time we know that, the part of the stack trace relevant to the // panic has been discarded. if r := recover(); r != nil { c.err = newPanicError(r) } } }()
c.val, c.err = fn() normalReturn = true }()
if !normalReturn { recovered = true } }
// 与Do类似,不再赘述 func(g *Group) DoChan(key string, fn func() (interface{}, error)) <-chan Result { ch := make(chan Result, 1) g.mu.Lock() if g.m == nil { g.m = make(map[string]*call) } if c, ok := g.m[key]; ok { c.dups++ c.chans = append(c.chans, ch) g.mu.Unlock() return ch } c := &call{chans: []chan<- Result{ch}} c.wg.Add(1) g.m[key] = c g.mu.Unlock() go g.doCall(c, key, fn)