context包通过父子链条关系和关闭chan来进行消息的通知
当一个context的done变量的chan被关闭后,它会关闭它的所有子context的done变量的chan,然后将子context设置为nil,并将自己从父context中移除
它的子context也会和它一样处理.但是并不需要从父context里面移除了,因为父context已经移除了
因此只要一个context的done变量的chan被关闭,它的儿子,孙子等context也会关闭
当chan被关闭后,就会唤醒所有等在它上面的goroutine.
type cancelCtx struct {
Context //父亲的context
mu sync.Mutex // 锁
done atomic.Value // 保存一个只读的chan,当保存的chan为关闭的时候就会唤醒所有等在它上面的groutine.
children map[canceler]struct{} // context的子context,当前context会将它的儿子的chan都关闭,并且设置为nil
err error // set to non-nil by the first cancel call
cause error // set to non-nil by the first cancel call
}
// cancel closes c.done, cancels each of c's children, and, if
// removeFromParent is true, removes c from its parent's children.
// cancel sets c.cause to cause if this is the first time c is canceled.
func (c *cancelCtx) cancel(removeFromParent bool, err, cause error) {
if err == nil {
panic("context: internal error: missing cancel error")
}
if cause == nil {
cause = err
}
c.mu.Lock()
if c.err != nil {
c.mu.Unlock()
return // already canceled
}
c.err = err
c.cause = cause
d, _ := c.done.Load().(chan struct{})
//关闭当前context的chan
if d == nil {
c.done.Store(closedchan)
} else {
close(d)
}
//关闭所有子context的chan
for child := range c.children {
// NOTE: acquiring the child's lock while holding parent's lock.
child.cancel(false, err, cause)
}
//移除所有子context
c.children = nil
c.mu.Unlock()
//是否要从父context中移除
if removeFromParent {
removeChild(c.Context, c)
}
}
context设置值通过valueCtx,里面有key和value,一般是通过层层往上找,找到第一个valueCtx中符合要求的key
type valueCtx struct {
Context
key, val any //键值对
}
valueC
1.context查找对应key的value,假如当前context为valueContext,则如果key匹配就返回对应的value,如果key不匹配,就返回其父context,继续找
2.假如context是backgroundCtx, todoCtx,则直接返回nil
3.如果是其它类型的context,假如是key是&cancelCtxKey,则直接返回当前context,否则返回其父context,继续找
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 withoutCancelCtx:
if key == &cancelCtxKey {
// This implements Cause(ctx) == nil
// when ctx is created using WithoutCancel.
return nil
}
c = ctx.c
case *timerCtx:
if key == &cancelCtxKey {
return &ctx.cancelCtx
}
c = ctx.Context
case backgroundCtx, todoCtx:
return nil
default:
return c.Value(key)
}
}
}