golang context 实现

235 阅读1分钟

context 主要分两种context 分为valueContext 和 cancelContext

value context 的结构如下

type valueCtx struct {
	Context
	key, val any
}
// 新建一个value Context
func WithValue(parent Context, key, val any) Context {
	if parent == nil {
		panic("cannot create context from nil parent")
	}
	if key == nil {
		panic("nil key")
	}
	if !reflectlite.TypeOf(key).Comparable() {
		panic("key is not comparable")
	}
	return &valueCtx{parent, key, val}
}
// 查找一个value
func (c *valueCtx) Value(key any) any {
	if c.key == key {
		return c.val
	}
	return value(c.Context, key)
}

func value(c Context, key any) any {
	for { // 类似于递归
		switch ctx := c.(type) {
		case *valueCtx:
			if key == ctx.key {
				return ctx.val
			}
			c = ctx.Context
		case *cancelCtx:
			if key == &cancelCtxKey {
				return c
			}
			c = ctx.Context
		case *timerCtx:
			if key == &cancelCtxKey {
				return ctx.cancelCtx
			}
			c = ctx.Context
		case *emptyCtx:
			return nil
		default:
			return c.Value(key)
		}
	}
}

查找value的逻辑是:先查自己的context key 是不是想等,如果没有就递归的去查他的parent 可以想象成一棵树,查找可以自底向上查找,但不能从上向下查找。换句话说,父节点看不到子节点设置的值,但子节点可以看到父节点的值

接口

type Context interface {
    Deadline() (deadline time.Time, ok bool)
    Done() <-chan struct{}
    Err() error
    Value(key any) any

}

type canceler interface {
	cancel(removeFromParent bool, err, cause error)
	Done() <-chan struct{}
}

cancelContext 当他被取消时,也会取消所有的孩子节点

type cancelCtx struct {
	Context

	mu       sync.Mutex            // protects following fields
	done     atomic.Value          // of 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
	cause    error                 // set to non-nil by the first cancel call
}

cancel 函数 总体流程如下,删除了一些代码,留了一些核心流程

func (c *cancelCtx) cancel(removeFromParent bool, err, cause error) {
	close(c.done)
	for child := range c.children {
		// NOTE: acquiring the child's lock while holding parent's lock.
		child.cancel(false, err, cause)
	}
	c.children = nil

	if removeFromParent {
		removeChild(c.Context, c)
	}
}

func parentCancelCtx(parent Context) (cancelCtx, bool) 从parent位置沿着父级不断的向上查找,直到遇到第一个cancelCtx或者不存这样的*cancelCtx

cloud.tencent.com/developer/a…