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 结构。

 

posted @ 2020-05-27 22:30  是的哟  阅读(206)  评论(0编辑  收藏  举报