301 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
		
		
			
		
	
	
			301 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| 
								 | 
							
								// Copyright 2014 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.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// +build !go1.7
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								package context
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								import (
							 | 
						||
| 
								 | 
							
									"errors"
							 | 
						||
| 
								 | 
							
									"fmt"
							 | 
						||
| 
								 | 
							
									"sync"
							 | 
						||
| 
								 | 
							
									"time"
							 | 
						||
| 
								 | 
							
								)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// An emptyCtx is never canceled, has no values, and has no deadline. It is not
							 | 
						||
| 
								 | 
							
								// struct{}, since vars of this type must have distinct addresses.
							 | 
						||
| 
								 | 
							
								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)
							 | 
						||
| 
								 | 
							
								)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Canceled is the error returned by Context.Err when the context is canceled.
							 | 
						||
| 
								 | 
							
								var Canceled = errors.New("context canceled")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// DeadlineExceeded is the error returned by Context.Err when the context's
							 | 
						||
| 
								 | 
							
								// deadline passes.
							 | 
						||
| 
								 | 
							
								var DeadlineExceeded = errors.New("context deadline exceeded")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// WithCancel returns a copy of parent with a new Done channel. The returned
							 | 
						||
| 
								 | 
							
								// context's Done channel is closed when the returned cancel function is called
							 | 
						||
| 
								 | 
							
								// or when the parent context's Done channel is closed, whichever happens first.
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// Canceling this context releases resources associated with it, so code should
							 | 
						||
| 
								 | 
							
								// call cancel as soon as the operations running in this Context complete.
							 | 
						||
| 
								 | 
							
								func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
							 | 
						||
| 
								 | 
							
									c := newCancelCtx(parent)
							 | 
						||
| 
								 | 
							
									propagateCancel(parent, c)
							 | 
						||
| 
								 | 
							
									return c, func() { c.cancel(true, Canceled) }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// newCancelCtx returns an initialized cancelCtx.
							 | 
						||
| 
								 | 
							
								func newCancelCtx(parent Context) *cancelCtx {
							 | 
						||
| 
								 | 
							
									return &cancelCtx{
							 | 
						||
| 
								 | 
							
										Context: parent,
							 | 
						||
| 
								 | 
							
										done:    make(chan struct{}),
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// propagateCancel arranges for child to be canceled when parent is.
							 | 
						||
| 
								 | 
							
								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]bool)
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											p.children[child] = true
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										p.mu.Unlock()
							 | 
						||
| 
								 | 
							
									} else {
							 | 
						||
| 
								 | 
							
										go func() {
							 | 
						||
| 
								 | 
							
											select {
							 | 
						||
| 
								 | 
							
											case <-parent.Done():
							 | 
						||
| 
								 | 
							
												child.cancel(false, parent.Err())
							 | 
						||
| 
								 | 
							
											case <-child.Done():
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}()
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// parentCancelCtx follows a chain of parent references until it finds a
							 | 
						||
| 
								 | 
							
								// *cancelCtx. This function understands how each of the concrete types in this
							 | 
						||
| 
								 | 
							
								// package represents its parent.
							 | 
						||
| 
								 | 
							
								func parentCancelCtx(parent Context) (*cancelCtx, bool) {
							 | 
						||
| 
								 | 
							
									for {
							 | 
						||
| 
								 | 
							
										switch c := parent.(type) {
							 | 
						||
| 
								 | 
							
										case *cancelCtx:
							 | 
						||
| 
								 | 
							
											return c, true
							 | 
						||
| 
								 | 
							
										case *timerCtx:
							 | 
						||
| 
								 | 
							
											return c.cancelCtx, true
							 | 
						||
| 
								 | 
							
										case *valueCtx:
							 | 
						||
| 
								 | 
							
											parent = c.Context
							 | 
						||
| 
								 | 
							
										default:
							 | 
						||
| 
								 | 
							
											return nil, false
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// removeChild removes a context from its parent.
							 | 
						||
| 
								 | 
							
								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()
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// A canceler is a context type that can be canceled directly. The
							 | 
						||
| 
								 | 
							
								// implementations are *cancelCtx and *timerCtx.
							 | 
						||
| 
								 | 
							
								type canceler interface {
							 | 
						||
| 
								 | 
							
									cancel(removeFromParent bool, err error)
							 | 
						||
| 
								 | 
							
									Done() <-chan struct{}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// A cancelCtx can be canceled. When canceled, it also cancels any children
							 | 
						||
| 
								 | 
							
								// that implement canceler.
							 | 
						||
| 
								 | 
							
								type cancelCtx struct {
							 | 
						||
| 
								 | 
							
									Context
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									done chan struct{} // closed by the first cancel call.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									mu       sync.Mutex
							 | 
						||
| 
								 | 
							
									children map[canceler]bool // set to nil by the first cancel call
							 | 
						||
| 
								 | 
							
									err      error             // set to non-nil by the first cancel call
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (c *cancelCtx) Done() <-chan struct{} {
							 | 
						||
| 
								 | 
							
									return c.done
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (c *cancelCtx) Err() error {
							 | 
						||
| 
								 | 
							
									c.mu.Lock()
							 | 
						||
| 
								 | 
							
									defer c.mu.Unlock()
							 | 
						||
| 
								 | 
							
									return c.err
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (c *cancelCtx) String() string {
							 | 
						||
| 
								 | 
							
									return fmt.Sprintf("%v.WithCancel", c.Context)
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// cancel closes c.done, cancels each of c's children, and, if
							 | 
						||
| 
								 | 
							
								// removeFromParent is true, removes c from its parent's children.
							 | 
						||
| 
								 | 
							
								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
							 | 
						||
| 
								 | 
							
									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)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// WithDeadline returns a copy of the parent context with the deadline adjusted
							 | 
						||
| 
								 | 
							
								// to be no later than d. If the parent's deadline is already earlier than d,
							 | 
						||
| 
								 | 
							
								// WithDeadline(parent, d) is semantically equivalent to parent. The returned
							 | 
						||
| 
								 | 
							
								// context's Done channel is closed when the deadline expires, when the returned
							 | 
						||
| 
								 | 
							
								// cancel function is called, or when the parent context's Done channel is
							 | 
						||
| 
								 | 
							
								// closed, whichever happens first.
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// Canceling this context releases resources associated with it, so code should
							 | 
						||
| 
								 | 
							
								// call cancel as soon as the operations running in this Context complete.
							 | 
						||
| 
								 | 
							
								func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) {
							 | 
						||
| 
								 | 
							
									if cur, ok := parent.Deadline(); ok && cur.Before(deadline) {
							 | 
						||
| 
								 | 
							
										// The current deadline is already sooner than the new one.
							 | 
						||
| 
								 | 
							
										return WithCancel(parent)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									c := &timerCtx{
							 | 
						||
| 
								 | 
							
										cancelCtx: newCancelCtx(parent),
							 | 
						||
| 
								 | 
							
										deadline:  deadline,
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									propagateCancel(parent, c)
							 | 
						||
| 
								 | 
							
									d := deadline.Sub(time.Now())
							 | 
						||
| 
								 | 
							
									if d <= 0 {
							 | 
						||
| 
								 | 
							
										c.cancel(true, DeadlineExceeded) // deadline has already passed
							 | 
						||
| 
								 | 
							
										return c, func() { c.cancel(true, Canceled) }
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									c.mu.Lock()
							 | 
						||
| 
								 | 
							
									defer c.mu.Unlock()
							 | 
						||
| 
								 | 
							
									if c.err == nil {
							 | 
						||
| 
								 | 
							
										c.timer = time.AfterFunc(d, func() {
							 | 
						||
| 
								 | 
							
											c.cancel(true, DeadlineExceeded)
							 | 
						||
| 
								 | 
							
										})
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return c, func() { c.cancel(true, Canceled) }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// A timerCtx carries a timer and a deadline. It embeds a cancelCtx to
							 | 
						||
| 
								 | 
							
								// implement Done and Err. It implements cancel by stopping its timer then
							 | 
						||
| 
								 | 
							
								// delegating to cancelCtx.cancel.
							 | 
						||
| 
								 | 
							
								type timerCtx struct {
							 | 
						||
| 
								 | 
							
									*cancelCtx
							 | 
						||
| 
								 | 
							
									timer *time.Timer // Under cancelCtx.mu.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									deadline time.Time
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (c *timerCtx) Deadline() (deadline time.Time, ok bool) {
							 | 
						||
| 
								 | 
							
									return c.deadline, true
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (c *timerCtx) String() string {
							 | 
						||
| 
								 | 
							
									return fmt.Sprintf("%v.WithDeadline(%s [%s])", c.cancelCtx.Context, c.deadline, c.deadline.Sub(time.Now()))
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (c *timerCtx) cancel(removeFromParent bool, err error) {
							 | 
						||
| 
								 | 
							
									c.cancelCtx.cancel(false, err)
							 | 
						||
| 
								 | 
							
									if removeFromParent {
							 | 
						||
| 
								 | 
							
										// Remove this timerCtx from its parent cancelCtx's children.
							 | 
						||
| 
								 | 
							
										removeChild(c.cancelCtx.Context, c)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									c.mu.Lock()
							 | 
						||
| 
								 | 
							
									if c.timer != nil {
							 | 
						||
| 
								 | 
							
										c.timer.Stop()
							 | 
						||
| 
								 | 
							
										c.timer = nil
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									c.mu.Unlock()
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)).
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// Canceling this context releases resources associated with it, so code should
							 | 
						||
| 
								 | 
							
								// call cancel as soon as the operations running in this Context complete:
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// 	func slowOperationWithTimeout(ctx context.Context) (Result, error) {
							 | 
						||
| 
								 | 
							
								// 		ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond)
							 | 
						||
| 
								 | 
							
								// 		defer cancel()  // releases resources if slowOperation completes before timeout elapses
							 | 
						||
| 
								 | 
							
								// 		return slowOperation(ctx)
							 | 
						||
| 
								 | 
							
								// 	}
							 | 
						||
| 
								 | 
							
								func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {
							 | 
						||
| 
								 | 
							
									return WithDeadline(parent, time.Now().Add(timeout))
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// WithValue returns a copy of parent in which the value associated with key is
							 | 
						||
| 
								 | 
							
								// val.
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// Use context Values only for request-scoped data that transits processes and
							 | 
						||
| 
								 | 
							
								// APIs, not for passing optional parameters to functions.
							 | 
						||
| 
								 | 
							
								func WithValue(parent Context, key interface{}, val interface{}) Context {
							 | 
						||
| 
								 | 
							
									return &valueCtx{parent, key, val}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// A valueCtx carries a key-value pair. It implements Value for that key and
							 | 
						||
| 
								 | 
							
								// delegates all other calls to the embedded Context.
							 | 
						||
| 
								 | 
							
								type valueCtx struct {
							 | 
						||
| 
								 | 
							
									Context
							 | 
						||
| 
								 | 
							
									key, val interface{}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (c *valueCtx) String() string {
							 | 
						||
| 
								 | 
							
									return fmt.Sprintf("%v.WithValue(%#v, %#v)", c.Context, c.key, c.val)
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (c *valueCtx) Value(key interface{}) interface{} {
							 | 
						||
| 
								 | 
							
									if c.key == key {
							 | 
						||
| 
								 | 
							
										return c.val
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return c.Context.Value(key)
							 | 
						||
| 
								 | 
							
								}
							 |