0、interface
type Context interface {Deadline() (deadline time.Time, ok bool)Done() <-chan struct{}Err() errorValue(key interface{}) interface{}}// 传播context// 主要是挂载到最近的为cancel context的父节点func propagateCancel(parent Context, child canceler) {done := parent.Done()if done == nil {return // 一般为root context}// 父节点是否已经被取消select {case <-done:// parent已经被cancelchild.cancel(false, parent.Err())returndefault:}// 若ok为true,则找到并且为cancel context的父节点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 {// 启动单独的g监听父节点的done信息atomic.AddInt32(&goroutines, +1)go func() {select {case <-parent.Done():child.cancel(false, parent.Err())case <-child.Done():}}()}}// 获取parent cancel contextfunc parentCancelCtx(parent Context) (*cancelCtx, bool) {// 此处逻辑有点向单例模式// 此处的判断也是为了提高效率done := parent.Done()// 若parent已经cancel或者为root context// 直接返回if done == closedchan || done == nil {return nil, false}// 通过&cancelCtxKey判断是否为cancel contextp, ok := parent.Value(&cancelCtxKey).(*cancelCtx)if !ok {return nil, false}// 上锁判断是否关闭p.mu.Lock()ok = p.done == donep.mu.Unlock()if !ok {return nil, false}return p, true}// 从parent child里面移除childfunc removeChild(parent Context, child canceler) {p, ok := parentCancelCtx(parent)if !ok {return}p.mu.Lock()if p.children != nil {delete(p.children, child)}p.mu.Unlock()}func contextName(c Context) string {if s, ok := c.(stringer); ok {return s.String()}return reflectlite.TypeOf(c).String()}
1、root context
其实就是一个空context
type emptyCtx intfunc (*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}func (e *emptyCtx) String() string {switch e {case background:return "context.Background"case todo:return "context.TODO"}return "unknown empty Context"}var (background = new(emptyCtx)todo = new(emptyCtx))func Background() Context {return background}func TODO() Context {return todo}
2、cancel context
var Canceled = errors.New("context canceled")// 可重复利用var closedchan = make(chan struct{})// 为了return cancelCtx自己(用&cancelCtxKey)var cancelCtxKey intfunc init() {close(closedchan)}type canceler interface {cancel(removeFromParent bool, err error)Done() <-chan struct{}}// 丢弃goroutine安全的result// goroutine安全// 幂等type CancelFunc func()type stringer interface {String() string}type cancelCtx struct {// parent contextContext// 锁,保护如下3个属性mu sync.Mutexdone chan struct{} // 懒式创建children map[canceler]struct{} // 子contexterr error // cacel error}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}}// 获取value,若key为&cancelCtxKey返回自己// 否则调用parent context获取func (c *cancelCtx) Value(key interface{}) interface{} {if key == &cancelCtxKey {return c}return c.Context.Value(key)}// context是否被取消的chan// 若调用cancel,则close chanfunc (c *cancelCtx) Done() <-chan struct{} {c.mu.Lock()if c.done == nil {c.done = make(chan struct{})}d := c.donec.mu.Unlock()return d}// 返回context的错误信息func (c *cancelCtx) Err() error {c.mu.Lock()err := c.errc.mu.Unlock()return err}// context名称func (c *cancelCtx) String() string {return contextName(c.Context) + ".WithCancel"}func (c *cancelCtx) cancel(removeFromParent bool, err error) {// err必须设置if err == nil {panic("context: internal error: missing cancel error")}c.mu.Lock()// err不为nil,则context已经取消if c.err != nil {c.mu.Unlock()return}c.err = err// 设置done chan为可复用的closedchan// 此处之所以为空是因为有可能用户没有调用Done就调用了cancelif c.done == nil {c.done = closedchan} else {close(c.done)}// 依次关闭子节点// 并置child为nilfor child := range c.children {child.cancel(false, err)}c.children = nilc.mu.Unlock()// 若需要移除,则从父parent移除if removeFromParent {removeChild(c.Context, c)}}
3、timeout/deadline context
var DeadlineExceeded error = deadlineExceededError{}type deadlineExceededError struct{}func (deadlineExceededError) Error() string { return "context deadline exceeded" }func (deadlineExceededError) Timeout() bool { return true }func (deadlineExceededError) Temporary() bool { return true }type timerCtx struct {cancelCtxtimer *time.Timer // Under cancelCtx.mu.deadline time.Time}// 指定Deadline的context,到期之后timer会自动关闭contextfunc WithDeadline(parent Context, d time.Time) (Context, CancelFunc) {// 父context不能为空if parent == nil {panic("cannot create context from nil parent")}// 如果父节点的 dealine 更靠前,那当然以父节点的为准,当前节点的 deadline 可以抛弃if cur, ok := parent.Deadline(); ok && cur.Before(d) {return WithCancel(parent)}c := &timerCtx{cancelCtx: newCancelCtx(parent),deadline: d,}// 传播contextpropagateCancel(parent, c)// deadline已经到期则直接canceldur := time.Until(d)if dur <= 0 {c.cancel(true, DeadlineExceeded)return c, func() { c.cancel(false, Canceled) }}// 否则增加timerc.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))}func (c *timerCtx) Deadline() (deadline time.Time, ok bool) {return c.deadline, true}func (c *timerCtx) String() string {return contextName(c.cancelCtx.Context) + ".WithDeadline(" +c.deadline.String() + " [" +time.Until(c.deadline).String() + "])"}func (c *timerCtx) cancel(removeFromParent bool, err error) {// 调用cancel方法c.cancelCtx.cancel(false, err)// 移除if removeFromParent {removeChild(c.cancelCtx.Context, c)}// 关闭timerc.mu.Lock()if c.timer != nil {c.timer.Stop()c.timer = nil}c.mu.Unlock()}
4、value context
func WithValue(parent Context, key, val interface{}) 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}}// 保存key/valuetype valueCtx struct {Contextkey, val interface{}}func (c *valueCtx) String() string {return contextName(c.Context) + ".WithValue(type " +reflectlite.TypeOf(c.key).String() +", val " + stringify(c.val) + ")"}func (c *valueCtx) Value(key interface{}) interface{} {if c.key == key {return c.val}return c.Context.Value(key)}func stringify(v interface{}) string {switch s := v.(type) {case stringer:return s.String()case string:return s}return "<not Stringer>"}
