| // Copyright 2020 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| // This is a copy of context.Context with all dependencies on "time" and "fmt" |
| // removed in order to avoid a dependency cycle. |
| // |
| // TODO(fxb/43246) Break the dependency cycle when using context.Context and |
| // use that instead. |
| |
| package context |
| |
| import ( |
| "errors" |
| "reflect" |
| "sync" |
| ) |
| |
| type Context interface { |
| Done() <-chan struct{} |
| |
| Err() error |
| |
| Value(key interface{}) interface{} |
| } |
| |
| var Canceled = errors.New("context canceled") |
| |
| type emptyCtx int |
| |
| 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 |
| } |
| |
| type CancelFunc func() |
| |
| func WithCancel(parent Context) (ctx Context, cancel CancelFunc) { |
| c := newCancelCtx(parent) |
| propagateCancel(parent, &c) |
| return &c, func() { c.cancel(true, Canceled) } |
| } |
| |
| func newCancelCtx(parent Context) cancelCtx { |
| return cancelCtx{Context: parent} |
| } |
| |
| 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 { |
| // parent has already been canceled |
| child.cancel(false, p.err) |
| } 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(): |
| } |
| }() |
| } |
| } |
| |
| func parentCancelCtx(parent Context) (*cancelCtx, bool) { |
| for { |
| switch c := parent.(type) { |
| case *cancelCtx: |
| return c, true |
| case *valueCtx: |
| parent = c.Context |
| default: |
| return nil, false |
| } |
| } |
| } |
| |
| 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() |
| } |
| |
| type canceler interface { |
| cancel(removeFromParent bool, err error) |
| Done() <-chan struct{} |
| } |
| |
| var closedchan = make(chan struct{}) |
| |
| func init() { |
| close(closedchan) |
| } |
| |
| type cancelCtx struct { |
| Context |
| |
| mu sync.Mutex |
| done chan struct{} |
| children map[canceler]struct{} |
| err error |
| } |
| |
| 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 |
| } |
| |
| func (c *cancelCtx) Err() error { |
| c.mu.Lock() |
| err := c.err |
| c.mu.Unlock() |
| return err |
| } |
| |
| 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 { |
| // NOTE: acquiring the child's lock while holding parent's lock. |
| child.cancel(false, err) |
| } |
| c.children = nil |
| c.mu.Unlock() |
| |
| if removeFromParent { |
| removeChild(c.Context, c) |
| } |
| } |
| |
| func WithValue(parent Context, key, val interface{}) Context { |
| if key == nil { |
| panic("nil key") |
| } |
| if !reflect.TypeOf(key).Comparable() { |
| panic("key is not comparable") |
| } |
| return &valueCtx{parent, key, val} |
| } |
| |
| 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) |
| } |