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) | ||
|  | } |