Go语言源码context解读,更优雅的上下文控制方式
func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
if parent == nil {
panic("cannot create context from nil parent")
}
c := newCancelCtx(parent)
propagateCancel(parent, &c)
return &c, func() { c.cancel(true, Canceled) }
}
func newCancelCtx(parent Context) cancelCtx {
return cancelCtx{Context: parent}
}
func propagateCancel(parent Context, child canceler) {
done := parent.Done()
if done == nil {
// 父节点不会退出,也就是不可退出类型的context
return
}
select {
case <-done:
// 父节点已经退出,递归退出子节点
child.cancel(false, parent.Err())
return
default:
}
if p, ok := parentCancelCtx(parent); ok {
p.mu.Lock()
if p.err != nil {
// 双重验证
// 父节点已经退出,递归退出子节点
child.cancel(false, p.err)
} else {
if p.children == nil {
// 懒式创建
p.children = make(map[canceler]struct{})
}
// 将当前节点放入父节点的子节点列表,用于后续退出
p.children[child] = struct{}{}
}
p.mu.Unlock()
} else {
go func() {
select {
case <-parent.Done():
child.cancel(false, parent.Err())
case <-child.Done():
}
}()
}
}
func parentCancelCtx(parent Context) (*cancelCtx, bool) {
done := parent.Done()
if done == closedchan || done == nil {
// 父节点已经关闭 或者 父节点不能被关闭
return nil, false
}
p, ok := parent.Value(&cancelCtxKey).(*cancelCtx)
if !ok {
return nil, false
}
p.mu.Lock()
// 双重验证
ok = p.done == done
p.mu.Unlock()
if !ok {
return nil, false
}
return p, true
}
func (c *cancelCtx) cancel(removeFromParent bool, err error) {
if err == nil {
panic("context: internal error: missing cancel error")
}
c.mu.Lock()
if c.err != nil {
// 如果包含错误,说明已经退出
c.mu.Unlock()
return
}
c.err = err
if c.done == nil {
c.done = closedchan
} else {
close(c.done)
}
for child := range c.children {
// NOTE: acquiring the child's lock while holding parent's lock.
child.cancel(false, err)
}
c.children = nil
c.mu.Unlock()
if removeFromParent {
removeChild(c.Context, c)
}
}
延伸阅读: