golang底层 context
接口
type Context interface { Deadline() (deadline time.Time, ok bool) //若实现了超时控制,该方法返回超时时间,true。否则ok为false Done() <-chan struct{} // 通过关闭channel进行通知 Err() error // 取消返回Canceled,超时返回DeadlineExceeded Value(key interface{}) interface{} } type canceler interface { cancel(removeFromParent bool, err error) Done() <-chan struct{} }
emptyCtx
type emptyCtx int // 不具备功能,存在的目的就是作为Context对象树的root节点 func (*emptyCtx) Deadline() (deadline time.Time, ok bool) { return } func (*emptyCtx) Done() <-chan struct{} { return nil } func (*emptyCtx) Err() error { return nil } func (*emptyCtx) Value(key interface{}) interface{} { return nil } var ( background = new(emptyCtx) todo = new(emptyCtx) ) func Background() Context { return background } func TODO() Context { return todo }
cancelCtx
type cancelCtx struct { // 内部封装了Context,实现了cancel方法,可以传递cancel消息 Context mu sync.Mutex // protects following fields done chan struct{} // created lazily, closed by first cancel call children map[canceler]struct{} // set to nil by the first cancel call err error // set to non-nil by the first cancel call } func (c *cancelCtx) Done() <-chan struct{} { // 初始化并返回 c.done c.mu.Lock() if c.done == nil { c.done = make(chan struct{}) } d := c.done c.mu.Unlock() return d }
cancel 方法:
若外部 err 为空,则代表这是一个非法的 cancel 操作,抛出 panic;
若 cancelCtx 内部 err 不为空,说明该 Ctx 已经执行过 cancel 操作,直接返回;
关闭 done channel,关联该 Ctx 的 goroutine 收到退出通知;
遍历 children,若有的话,执行 child.cancel 操作;
调用 removeChild 将自己从 parent context 中移除。
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 // already canceled } c.err = err if c.done == nil { c.done = closedchan } else { close(c.done) } for child := range c.children { child.cancel(false, err) // NOTE: acquiring the child's lock while holding parent's lock. } c.children = nil c.mu.Unlock() if removeFromParent { removeChild(c.Context, c) } } func WithCancel(parent Context) (ctx Context, cancel CancelFunc) { c := newCancelCtx(parent) propagateCancel(parent, &c) //尝试向上找到 *cancelCtx ,将&c插入该*cancelCtx的children里 return &c, func() { c.cancel(true, Canceled) } } func newCancelCtx(parent Context) cancelCtx { return cancelCtx{Context: parent} } type CancelFunc func()
timerCtx
type timerCtx struct { // 内部封装了cancelCtx,可以定时退出 cancelCtx timer *time.Timer // Under cancelCtx.mu. deadline time.Time } func (c *timerCtx) Deadline() (deadline time.Time, ok bool) { return c.deadline, true } func (c *timerCtx) cancel(removeFromParent bool, err error) { c.cancelCtx.cancel(false, err) if removeFromParent { removeChild(c.cancelCtx.Context, c) // Remove this timerCtx from its parent cancelCtx's children. } c.mu.Lock() if c.timer != nil { c.timer.Stop() c.timer = nil } c.mu.Unlock() } func WithDeadline(parent Context, d time.Time) (Context, CancelFunc) { if cur, ok := parent.Deadline(); ok && cur.Before(d) { return WithCancel(parent) } c := &timerCtx{ cancelCtx: newCancelCtx(parent), deadline: d, } propagateCancel(parent, c) dur := time.Until(d) if dur <= 0 { c.cancel(true, DeadlineExceeded) // deadline has already passed return c, func() { c.cancel(false, Canceled) } } c.mu.Lock() defer c.mu.Unlock() if c.err == nil { c.timer = time.AfterFunc(dur, func() { c.cancel(true, DeadlineExceeded) }) } return c, func() { c.cancel(true, Canceled) } } func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) { return WithDeadline(parent, time.Now().Add(timeout)) }
valueCtx
type valueCtx struct { // 用于传递消息 Context key, val interface{} } func (c *valueCtx) Value(key interface{}) interface{} { if c.key == key { return c.val } return c.Context.Value(key) } func WithValue(parent Context, key, val interface{}) Context { if key == nil { panic("nil key") } if !reflectlite.TypeOf(key).Comparable() { panic("key is not comparable") } return &valueCtx{parent, key, val} }
其他函数
propagateCancel
接收 parent context 和 child canceler方法,若 parent为emptyCtx,则不注册;
否则通过 parentCancelCtx 寻找最近的一个 *cancelCtx ;
若该 *cancelCtx 已经结束,则调用 child 的 cancel 方法,否则向该 *cancelCtx 注册 child 。
func propagateCancel(parent Context, child canceler) { if parent.Done() == nil { return } // parent is never canceled if p, ok := parentCancelCtx(parent); ok { p.mu.Lock() if p.err != nil { child.cancel(false, p.err)// parent has already been canceled } 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(): } }() } }
parentCancelCtx
从parentCtx 中向上迭代寻找第一个 cancelCtx 并返回。
从函数逻辑中可以看到,只有当 parent.(type) 为 *valueCtx 的时候,parent 才会向上迭代而不是立即返回。
否则该函数都是直接返回或返回经过包装的 *cancelCtx 。
func parentCancelCtx(parent Context) (*cancelCtx, bool) { for { switch c := parent.(type) { case *cancelCtx: return c, true case *timerCtx: return &c.cancelCtx, true case *valueCtx: parent = c.Context default: return nil, false } } }
valueCtx是如何存储children和parent context结构的?
parentCtx根本无需,也没有办法通过Done()方法通知valueCtx,valueCtx也没有额外实现Done()方法。可以理解为:valueCtx与parentCtx公用一个done channel,当parentCtx调用了cancel方法并关闭了done channel时,监听valueCtx的done channel的goroutine同样会收到退出信号。另外,当parentCtx没有实现cancel方法(如emptyCtx)时,可以认为valueCtx也是无法cancel的。
valueCtx为什么没有propagateCancel函数向parent context注册自己。既然没有注册,为何parent超时后能通知 valueCtx 一起退出?
valueCtx 依赖于 parentCtx 的 *cancelCtx 结构。