zoukankan      html  css  js  c++  java
  • 带小伙伴手写 golang context

    前言 - context 源码

      可以先了解官方 context.go 轮廓. 这里捎带保存一份当前 context 版本备份. 

    // 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.
    
    // Package context defines the Context type, which carries deadlines,
    // cancellation signals, and other request-scoped values across API boundaries
    // and between processes.
    //
    // Incoming requests to a server should create a Context, and outgoing
    // calls to servers should accept a Context. The chain of function
    // calls between them must propagate the Context, optionally replacing
    // it with a derived Context created using WithCancel, WithDeadline,
    // WithTimeout, or WithValue. When a Context is canceled, all
    // Contexts derived from it are also canceled.
    //
    // The WithCancel, WithDeadline, and WithTimeout functions take a
    // Context (the parent) and return a derived Context (the child) and a
    // CancelFunc. Calling the CancelFunc cancels the child and its
    // children, removes the parent's reference to the child, and stops
    // any associated timers. Failing to call the CancelFunc leaks the
    // child and its children until the parent is canceled or the timer
    // fires. The go vet tool checks that CancelFuncs are used on all
    // control-flow paths.
    //
    // Programs that use Contexts should follow these rules to keep interfaces
    // consistent across packages and enable static analysis tools to check context
    // propagation:
    //
    // Do not store Contexts inside a struct type; instead, pass a Context
    // explicitly to each function that needs it. The Context should be the first
    // parameter, typically named ctx:
    //
    // 	func DoSomething(ctx context.Context, arg Arg) error {
    // 		// ... use ctx ...
    // 	}
    //
    // Do not pass a nil Context, even if a function permits it. Pass context.TODO
    // if you are unsure about which Context to use.
    //
    // Use context Values only for request-scoped data that transits processes and
    // APIs, not for passing optional parameters to functions.
    //
    // The same Context may be passed to functions running in different goroutines;
    // Contexts are safe for simultaneous use by multiple goroutines.
    //
    // See https://blog.golang.org/context for example code for a server that uses
    // Contexts.
    package context
    
    import (
    	"errors"
    	"internal/reflectlite"
    	"sync"
    	"time"
    )
    
    // A Context carries a deadline, a cancellation signal, and other values across
    // API boundaries.
    //
    // Context's methods may be called by multiple goroutines simultaneously.
    type Context interface {
    	// Deadline returns the time when work done on behalf of this context
    	// should be canceled. Deadline returns ok==false when no deadline is
    	// set. Successive calls to Deadline return the same results.
    	Deadline() (deadline time.Time, ok bool)
    
    	// Done returns a channel that's closed when work done on behalf of this
    	// context should be canceled. Done may return nil if this context can
    	// never be canceled. Successive calls to Done return the same value.
    	//
    	// WithCancel arranges for Done to be closed when cancel is called;
    	// WithDeadline arranges for Done to be closed when the deadline
    	// expires; WithTimeout arranges for Done to be closed when the timeout
    	// elapses.
    	//
    	// Done is provided for use in select statements:
    	//
    	//  // Stream generates values with DoSomething and sends them to out
    	//  // until DoSomething returns an error or ctx.Done is closed.
    	//  func Stream(ctx context.Context, out chan<- Value) error {
    	//  	for {
    	//  		v, err := DoSomething(ctx)
    	//  		if err != nil {
    	//  			return err
    	//  		}
    	//  		select {
    	//  		case <-ctx.Done():
    	//  			return ctx.Err()
    	//  		case out <- v:
    	//  		}
    	//  	}
    	//  }
    	//
    	// See https://blog.golang.org/pipelines for more examples of how to use
    	// a Done channel for cancellation.
    	Done() <-chan struct{}
    
    	// If Done is not yet closed, Err returns nil.
    	// If Done is closed, Err returns a non-nil error explaining why:
    	// Canceled if the context was canceled
    	// or DeadlineExceeded if the context's deadline passed.
    	// After Err returns a non-nil error, successive calls to Err return the same error.
    	Err() error
    
    	// Value returns the value associated with this context for key, or nil
    	// if no value is associated with key. Successive calls to Value with
    	// the same key returns the same result.
    	//
    	// Use context values only for request-scoped data that transits
    	// processes and API boundaries, not for passing optional parameters to
    	// functions.
    	//
    	// A key identifies a specific value in a Context. Functions that wish
    	// to store values in Context typically allocate a key in a global
    	// variable then use that key as the argument to context.WithValue and
    	// Context.Value. A key can be any type that supports equality;
    	// packages should define keys as an unexported type to avoid
    	// collisions.
    	//
    	// Packages that define a Context key should provide type-safe accessors
    	// for the values stored using that key:
    	//
    	// 	// Package user defines a User type that's stored in Contexts.
    	// 	package user
    	//
    	// 	import "context"
    	//
    	// 	// User is the type of value stored in the Contexts.
    	// 	type User struct {...}
    	//
    	// 	// key is an unexported type for keys defined in this package.
    	// 	// This prevents collisions with keys defined in other packages.
    	// 	type key int
    	//
    	// 	// userKey is the key for user.User values in Contexts. It is
    	// 	// unexported; clients use user.NewContext and user.FromContext
    	// 	// instead of using this key directly.
    	// 	var userKey key
    	//
    	// 	// NewContext returns a new Context that carries value u.
    	// 	func NewContext(ctx context.Context, u *User) context.Context {
    	// 		return context.WithValue(ctx, userKey, u)
    	// 	}
    	//
    	// 	// FromContext returns the User value stored in ctx, if any.
    	// 	func FromContext(ctx context.Context) (*User, bool) {
    	// 		u, ok := ctx.Value(userKey).(*User)
    	// 		return u, ok
    	// 	}
    	Value(key interface{}) interface{}
    }
    
    // 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 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 }
    
    // 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)
    )
    
    // Background returns a non-nil, empty Context. It is never canceled, has no
    // values, and has no deadline. It is typically used by the main function,
    // initialization, and tests, and as the top-level Context for incoming
    // requests.
    func Background() Context {
    	return background
    }
    
    // TODO returns a non-nil, empty Context. Code should use context.TODO when
    // it's unclear which Context to use or it is not yet available (because the
    // surrounding function has not yet been extended to accept a Context
    // parameter).
    func TODO() Context {
    	return todo
    }
    
    // A CancelFunc tells an operation to abandon its work.
    // A CancelFunc does not wait for the work to stop.
    // After the first call, subsequent calls to a CancelFunc do nothing.
    type CancelFunc func()
    
    // 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}
    }
    
    // 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]struct{})
    			}
    			p.children[child] = struct{}{}
    		}
    		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{}
    }
    
    // closedchan is a reusable closed channel.
    var closedchan = make(chan struct{})
    
    func init() {
    	close(closedchan)
    }
    
    // A cancelCtx can be canceled. When canceled, it also cancels any children
    // that implement canceler.
    type cancelCtx struct {
    	Context
    
    	mu       sync.Mutex            // protects following fields
    	done     chan struct{}         // created lazily, closed by first cancel call
    	children map[canceler]struct{} // 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{} {
    	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
    }
    
    type stringer interface {
    	String() string
    }
    
    func contextName(c Context) string {
    	if s, ok := c.(stringer); ok {
    		return s.String()
    	}
    	return reflectlite.TypeOf(c).String()
    }
    
    func (c *cancelCtx) String() string {
    	return contextName(c.Context) + ".WithCancel"
    }
    
    // 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
    	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)
    	}
    }
    
    // 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, d time.Time) (Context, CancelFunc) {
    	if cur, ok := parent.Deadline(); ok && cur.Before(d) {
    		// The current deadline is already sooner than the new one.
    		return WithCancel(parent)
    	}
    	c := &timerCtx{
    		cancelCtx: newCancelCtx(parent),
    		deadline:  d,
    	}
    	propagateCancel(parent, c)
    	dur := time.Until(d)
    	if dur <= 0 {
    		c.cancel(true, DeadlineExceeded) // deadline has already passed
    		return c, func() { c.cancel(false, Canceled) }
    	}
    	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) }
    }
    
    // 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 contextName(c.cancelCtx.Context) + ".WithDeadline(" +
    		c.deadline.String() + " [" +
    		time.Until(c.deadline).String() + "])"
    }
    
    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.
    //
    // The provided key must be comparable and should not be of type
    // string or any other built-in type to avoid collisions between
    // packages using context. Users of WithValue should define their own
    // types for keys. To avoid allocating when assigning to an
    // interface{}, context keys often have concrete type
    // struct{}. Alternatively, exported context key variables' static
    // type should be a pointer or interface.
    func WithValue(parent Context, key, val interface{}) Context {
    	if key == nil {
    		panic("nil key")
    	}
    	if !reflectlite.TypeOf(key).Comparable() {
    		panic("key is not comparable")
    	}
    	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{}
    }
    
    // stringify tries a bit to stringify v, without using fmt, since we don't
    // want context depending on the unicode tables. This is only used by
    // *valueCtx.String().
    func stringify(v interface{}) string {
    	switch s := v.(type) {
    	case stringer:
    		return s.String()
    	case string:
    		return s
    	}
    	return "<not Stringer>"
    }
    
    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)
    }  

    golang 标准库 1.7 版本引入 context 包, 用于 golang 函数链安全的管理和控制.   

    说真 golang context 实现非常漂亮, 代码中说明也事无巨细, 整体很赏心悦目.   

    那我们带大家宏观过一遍 context 设计布局. 

    // Context 上下文调用链条
    type Context interface {
    	// Deadline 返回是否完成和结束时刻
    	Deadline() (deadline time.Time, ok bool)
    
    	// Done 返回是否完成的阻塞 chan
    	Done() (done <-chan struct{})
    
    	// Err Done 时候储存 error 信息, Canceled or DeadlineExceeded
    	Err() (err error)
    
    	// Value Context 中获取 key 的值
    	Value(key interface{}) (val interface{})
    }
    
    // emptyCtx 永远不会被取消的 context
    type emptyCtx int
    
    // cancelCtx 可被取消的 context
    type cancelCtx struct { Context ... }
    
    // timerCtx 带计时器和截止日期的 cancelCtx
    type timerCtx struct { cancelCtx ... }
    
    // valueCtx 储存键值对 context
    type valueCtx struct {
    	Context
    
    	key, val interface{}
    }

    可以看出 Context 是个 interface, context.go 中有四种结构实现了 Context interface, 分别

    是 emptyCtx, cancelCtx, timerCtx, valueCtx. 细看这类数据结构设计思路, 只记录父亲是谁,

    单线联系. 可以反着类比普通树结构只记录儿子是谁哈哈, 大众货居然被玩出❀, 真强.

    正文 - context 手写

      废话不多说, 开始写代码. 上面 context.go 文件一把梭哈, 对于初学者还是无从学起的. 我们

    这里按照结构分模式展开书写, 

    ├── context
    │   ├── cancel.go
    │   ├── context.go
    │   ├── empty.go
    │   ├── timer.go
    │   ├── value.go
    │   └── with.go

    让其看起来好理解些. 

    context.go 

    package context
    
    import (
    	"errors"
    	"time"
    )
    
    // Canceled 取消上下文时 context.Err 返回的错误
    var Canceled = errors.New("context canceled")
    
    // DeadlineExceeded 上下文超时时候 Context.Err 返回的错误
    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 }
    
    // Context 上下文调用链条
    type Context interface {
    	// Deadline 返回是否完成和结束时刻
    	Deadline() (deadline time.Time, ok bool)
    
    	// Done 返回是否完成的阻塞 chan
    	Done() (done <-chan struct{})
    
    	// Err Done 时候储存 error 信息, Canceled or DeadlineExceeded
    	Err() (err error)
    
    	// Value Context 中获取 key 的值
    	Value(key interface{}) (val interface{})
    }
    

    empty.go

    package context
    
    import "time"
    
    // emptyCtx 永远不会被取消的 context
    type emptyCtx int
    
    func (*emptyCtx) Deadline() (deadline time.Time, ok bool) { return }
    func (*emptyCtx) Done() (done <-chan struct{})            { return }
    func (*emptyCtx) Err() (err error)                        { return }
    func (*emptyCtx) Value(key interface{}) (val interface{}) { return }
    
    var (
    	background = new(emptyCtx)
    	todo       = new(emptyCtx)
    )
    
    // String emptyCtx 打印方法
    func (e *emptyCtx) String() string {
    	switch e {
    	case background:
    		return "context.Background"
    	case todo:
    		return "context.TODO"
    	}
    	return "unknown empty Context"
    }
    
    // Background 不会被取消的 context, 一般用做顶级 context
    func Background() Context {
    	return background
    }
    
    // TODO 当你不知道用什么 Context 时候, 记住那就用这个
    func TODO() Context {
    	return todo
    }
    

    cancel.go 

    package context
    
    import (
    	"reflect"
    	"sync"
    )
    
    type stringer interface {
    	String() string
    }
    
    func contextName(c Context) string {
    	if s, ok := c.(stringer); ok {
    		return s.String()
    	}
    	return reflect.TypeOf(c).String()
    }
    
    // canceler 可以直接取消的上下文, 详情见 *cancelCtx 和 *timerCtx
    type canceler interface {
    	cancel(removeFromParent bool, err error)
    	Done() (done <-chan struct{})
    }
    
    // cancelCtx 可被取消的 context
    type cancelCtx struct {
    	Context
    
    	mu       sync.Mutex            // 互斥锁保证 goroutine 安全
    	done     chan struct{}         // 慢创建, 首次取消才会被调用的开关
    	children map[canceler]struct{} // 首次 context 取消后待取消的 child context
    	err      error                 // 首次取消 context 保留的 error
    }
    
    func (c *cancelCtx) Done() (done <-chan struct{}) {
    	c.mu.Lock()
    	if c.done == nil {
    		c.done = make(chan struct{})
    	}
    	done = c.done
    	c.mu.Unlock()
    	return
    }
    
    func (c *cancelCtx) Err() (err error) {
    	c.mu.Lock()
    	err = c.err
    	c.mu.Unlock()
    	return
    }
    
    func (c *cancelCtx) String() string {
    	return contextName(c) + ".WithCancel"
    }
    
    // newCancelCtx returns an initialized cancelCtx
    func newCancelCtx(parent Context) cancelCtx {
    	return cancelCtx{Context: parent}
    }
    

    (这里用 "reflect" 标准包来表述 "internal/reflectlite" 内部才能使用的 "reflect" 包)

    timer.go

    package context
    
    import (
    	"time"
    )
    
    // timerCtx 带计时器和截止日期的 cancelCtx
    type timerCtx struct {
    	cancelCtx
    
    	timer    *time.Timer // 依赖 cancelCtx.mu
    	deadline time.Time   // context 截止时间
    }
    
    func (c *timerCtx) Deadline() (time.Time, bool) {
    	return c.deadline, true
    }
    
    func (c *timerCtx) String() string {
    	return contextName(c.cancelCtx.Context) + ".WithDeadline(" + c.deadline.String() + " [" + time.Until(c.deadline).String() + "])"
    }
    

    value.go

    package context
    
    import "reflect"
    
    // valueCtx 储存键值对 context
    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)
    }
    
    // stringify tries a bit to stringify v, without using fmt, since we don't
    // want context depending on the unicode tables. This is only used by *valueCtx.String()
    func stringify(v interface{}) string {
    	switch s := v.(type) {
    	case stringer:
    		return s.String()
    	case string:
    		return s
    	}
    	return "<not Stringer>"
    }
    
    func (c *valueCtx) String() string {
    	return contextName(c.Context) + ".WithValue(type " + reflect.TypeOf(c.key).String() + ", val " + stringify(c.val) + ")"
    }
    

    with.go

    package context
    
    import (
    	"reflect"
    	"time"
    )
    
    // WithValue returns a copy of parent in which the value associated with key is val
    func WithValue(parent Context, key, val interface{}) Context {
    	if key == nil {
    		panic("nil key")
    	}
    
    	// key 不具备可比性时候, 会引发运行时恐慌 panic
    	if !reflect.TypeOf(key).Comparable() {
    		panic("key is not comparable")
    	}
    
    	return &valueCtx{parent, key, val}
    }
    
    // parentCancelCtx 获取 parent CancelCtx
    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()
    }
    
    // closedchan 可重复使用且已经关闭通道
    var closedchan = make(chan struct{})
    
    func init() {
    	close(closedchan)
    }
    
    // cancel cancelCtx 取消操作, 关闭 c.done, 取消每个 child context, removeFromParent is true 从 parent 删除 child
    func (c *cancelCtx) cancel(removeFromParent bool, err 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: 保留 parent 锁, 继续获取 child 锁
    		child.cancel(false, err)
    	}
    	c.children = nil
    	c.mu.Unlock()
    
    	if removeFromParent {
    		removeChild(c.Context, c)
    	}
    }
    
    // cancel timerCtx 取消操作
    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()
    }
    
    // propagateCancel parent 取消 map 中添加 child 子项
    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():
    			}
    		}()
    	}
    }
    
    // CancelFunc cancel func 行为
    type CancelFunc func()
    
    // WithCancel 基于 parent context 构造可取消的 child context
    func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
    	c := newCancelCtx(parent)
    	propagateCancel(parent, &c)
    	return &c, func() { c.cancel(true, Canceled) }
    }
    
    // WithDeadline 返回 child context 并调整 parent deadline
    func WithDeadline(parent Context, d time.Time) (Context, CancelFunc) {
    	if cur, ok := parent.Deadline(); ok && cur.Before(d) {
    		// 当前截止日期早于新设置的截止日期, 直接使用当前截止日期
    		return WithCancel(parent)
    	}
    
    	c := &timerCtx{
    		cancelCtx: newCancelCtx(parent),
    		deadline:  d,
    	}
    	propagateCancel(parent, c)
    	dur := time.Until(d)
    	if dur <= 0 {
    		c.cancel(true, DeadlineExceeded) // deadline has already passed
    		return c, func() { c.cancel(false, Canceled) }
    	}
    
    	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) }
    }
    
    // WithTimeout returns WithDeadline(parent, time.Now().Add(timeout))
    func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {
    	return WithDeadline(parent, time.Now().Add(timeout))
    } 

    看到 with.go 是不是有种 mmp 感觉, 第一问还好, 第二问也爽爽, 这第三问怎么就有了压轴最后一问思索感 ~ 

    其实还好, 多关怀下 propagateCancel 和 cancelCtx.cancel 操作怎么打配合.  其实上面源码中最难写的是

    reflect 和 time (errors 和 sync 接触多好理解) 有兴趣的同行可以深入研究 . 水文该说再见了, 希望大家有

    所得 ~ 

    后记 - 代码和注释并存

      错误是难免的, 欢迎勘误 ~ 共同成长提高 ❉

     <窗外雨>https://music.163.com/#/song?id=514543382

     

  • 相关阅读:
    python中gui编程的模块之一:tkinter(python3.x中是tkinter,小写的t)
    oracle中用户删除不了,ORA-01940提示 “无法删除当前已连接用户”
    python中一次性input3个整数,并用空格隔开怎么表示
    二、loadrunner参数化连接数据库
    一、loadrunner脚本录制及回放
    eclipse安装使用
    mysqld_safe与mysqld区别(转载)
    MySQL数据库用户基本管理
    Docker容器技术
    shell编程实战学习(4)
  • 原文地址:https://www.cnblogs.com/life2refuel/p/10803664.html
Copyright © 2011-2022 走看看