0、interface
type Context interface {
Deadline() (deadline time.Time, ok bool)
Done() <-chan struct{}
Err() error
Value(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已经被cancel
child.cancel(false, parent.Err())
return
default:
}
// 若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 context
func parentCancelCtx(parent Context) (*cancelCtx, bool) {
// 此处逻辑有点向单例模式
// 此处的判断也是为了提高效率
done := parent.Done()
// 若parent已经cancel或者为root context
// 直接返回
if done == closedchan || done == nil {
return nil, false
}
// 通过&cancelCtxKey判断是否为cancel context
p, ok := parent.Value(&cancelCtxKey).(*cancelCtx)
if !ok {
return nil, false
}
// 上锁判断是否关闭
p.mu.Lock()
ok = p.done == done
p.mu.Unlock()
if !ok {
return nil, false
}
return p, true
}
// 从parent child里面移除child
func 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 int
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
}
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 int
func 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 context
Context
// 锁,保护如下3个属性
mu sync.Mutex
done chan struct{} // 懒式创建
children map[canceler]struct{} // 子context
err 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 chan
func (c *cancelCtx) Done() <-chan struct{} {
c.mu.Lock()
if c.done == nil {
c.done = make(chan struct{})
}
d := c.done
c.mu.Unlock()
return d
}
// 返回context的错误信息
func (c *cancelCtx) Err() error {
c.mu.Lock()
err := c.err
c.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就调用了cancel
if c.done == nil {
c.done = closedchan
} else {
close(c.done)
}
// 依次关闭子节点
// 并置child为nil
for child := range c.children {
child.cancel(false, err)
}
c.children = nil
c.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 {
cancelCtx
timer *time.Timer // Under cancelCtx.mu.
deadline time.Time
}
// 指定Deadline的context,到期之后timer会自动关闭context
func 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,
}
// 传播context
propagateCancel(parent, c)
// deadline已经到期则直接cancel
dur := time.Until(d)
if dur <= 0 {
c.cancel(true, DeadlineExceeded)
return c, func() { c.cancel(false, Canceled) }
}
// 否则增加timer
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))
}
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)
}
// 关闭timer
c.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/value
type valueCtx struct {
Context
key, 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>"
}