All source code analysis in this article is based on Go 1.16.4.

1. Context Introduction

Context in the standard library is an interface with various implementations; Context was added to the standard library in Go 1.7 and is mainly used for setting deadlines, synchronizing signals, passing context request values, etc. across multiple Goroutines.

Because of the need to pass signals across multiple Goroutines, multiple Contexts often need to be associated together to form a tree like structure. This tree-like association requires a root Context, and then other Contexts are associated with the root Context as its child Context; this association can be multi-level, so there are three types of Contexts in the role:

  • root Context
  • parent Context
  • child Context

2. Context type

2.1. Creation of Context

The Context creation methods defined in the standard library are roughly as follows

  • context.Background(): This method is used to create the root Context and cannot be cancelled
  • context.TODO(): This method is also used to create the root Context (inaccurate) and cannot be cancelled, TODO usually means that you don’t know which Context to use, so it may be adjusted later
  • context.WithCancel(parent Context): Creates a child Context with a cancel method from the parent Context, which can be called manually
  • context.WithDeadline(parent Context, d time.Time): Creates a child Context with a cancel method from the parent Context, except that the Context will be automatically canceled when d time is reached
  • context.WithTimeout(parent Context, timeout time.Duration): similar to WithDeadline, except that it specifies a timeout from the current time
  • context.WithValue(parent Context, key, val interface{}): Creates a child Context from the parent Context that can store a key-value pair and is an uncancelable Context

2.2. Context internal type

After reading the sources, you will find that the various creation methods of Context actually use only 4 types of Context implementations

2.2.1 emptyCtx

emptyCtx is actually an int that returns nil for all the main implementations of the Context interface (Deadline, Done, Err, Value), i.e. it is actually a context that does “nothing”. Context; it is usually used to create root Context, which is what context.Background() and context.TODO() return in the standard library as emptyCtx.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
// 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
}

2.2.2. cancelCtx

cancelCtx contains an internal Context interface instance and a children map[canceler]struct{}; the purpose of these two variables is to ensure that cancelCtx can switch between the parent Context and child Context roles:

  • When acting as a parent Context for other Context instances, store the other Context instances in children map[canceler]struct{} to establish the association
  • When acting as a child Context of other Context instances, store the other Context instances in the “Context” variable to create the association

cancelCtx is defined as a Context that can be cancelled, and due to the tree structure of Context, when cancelling as parent Context, all child Contexts under the node need to be cancelled synchronously, so you just need to traverse children map[canceler]structure{} and cancel them one by one.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
// 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) Value(key interface{}) interface{} {
	if key == &cancelCtxKey {
		return c
	}
	return c.Context.Value(key)
}

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

2.2.3 timerCtx

timerCtx is actually built on top of cancelCtx, the only difference is the addition of a timer and a cutoff time; with these two configurations you can automatically cancel at a specific time, WithDeadline(parent Context, d time.Time) and WithTimeout(parent Context, timeout time.Duration) methods return this timerCtx.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 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()
}

2.2.4. valueCtx

valueCtx also contains an instance of the Context interface for the purpose of being a child Context, and two unrestricted variables key, val interface{}; when calling valueCtx.Value(key interface{}), a recursive lookup is performed, but this lookup is only responsible for finding the “immediate” Context, i.e. it is possible to recursively look up indefinitely whether parent Context contains this key, but it cannot find out if the sibling Context does.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 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)
}

3. cancelCtx Source Code Analysis

3.1. How cancelCtx is created

cancelCtx is created when the context.WithCancel method is called (disregarding other derived types for now), and is relatively simple to create:

1
2
3
4
5
6
7
8
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) }
}

The newCancelCtx method is to set the parent Context to an internal variable, and it is worth analyzing the propagateCancel(parent, &c) method and the parentCancelCtx(parent Context) (* cancelCtx, bool) method, these two methods ensure that the Context chain can be linked from the top to the bottom of the cancel, the analysis of these two methods is as follows:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
// propagateCancel This method is responsible for ensuring that when the parent Context is cancelled, the child Context will also be cancelled in conjunction
func propagateCancel(parent Context, child canceler) {
	// 针For Context(emptyCtx) created by context.Background()/TODO(), its done channel will always be nil
	// For other standard cancelable Contexts (cancelCtx, timerCtx) calling the Done() method will delay the initialization of the done channel (created when called)
	// So a done channel of nil means that the parent context must never be cancelled, so there is no need to link to the child Context
	done := parent.Done()
	if done == nil {
		return // parent is never canceled
	}

	// If the done channel is not nil, the parent Context is a Context that can be cancelled
	// Here you need to immediately determine whether the done channel can be read, if it can be read, it means that the above lock-free phase
	// parent Context has been cancelled, then the child Context should be cancelled immediately
	select {
	case <-done:
		// parent is already canceled
		child.cancel(false, parent.Err())
		return
	default:
	}

	// parentCancelCtx is used to get the underlying cancelable Context of the parent Context(cancelCtx)
	//
	// Returns true if the parent Context is itself *cancelCtx or a Context derived from cancelCtx in the standard library
	// Returns false if the parent Context has been cancelled / or cannot be cancelled at all
	// If the parent Context cannot be converted to a *cancelCtx it will also return false
	// If the parent Context is a custom deep wrapped cancelCtx (with its own defined done channel) then it will also return false
	if p, ok := parentCancelCtx(parent); ok { // ok is true means that the parent Context is a standard library cancelCtx or at least can be fully converted to *cancelCtx
		// Lock the parent Context first to prevent changes
		p.mu.Lock()
		// Because ok is true, it has been determined that the parent Context must be *cancelCtx, and cancelCtx must set err when canceling
		// So if the err of the parent Context is not empty in the case of concurrent locking, it has been cancelled
		if p.err != nil {
			// parent has already been canceled
			child.cancel(false, p.err)
		} else {
			// When ok is true, the parent Context must be *cancelCtx, and err is nil
			// This means that the parent Context has not been cancelled yet, so you have to associate the child Context with the children map of the parent Context
			// This children map will be traversed when the parent Context is cancelled and the child Context's cancel method will be called in bulk
			if p.children == nil {
				p.children = make(map[canceler]struct{})
			}
			p.children[child] = struct{}{}
		}
		p.mu.Unlock()
	} else { // ok is false, means: "parent Context has been cancelled" or "Cannot be cancelled at all" or "Cannot be converted to a *cancelCtx" or "Is a custom deep wrapped cancelCtx"
		atomic.AddInt32(&goroutines, +1)
		// Since the code determines at the beginning of the method that the parent Context is "cancelled" or "cannot be cancelled at all"
		// So these two cases do not happen here, so <-parent.Done() does not generate panic
		// 
		// The only remaining possibility is that the parent Context "cannot be converted to a *cancelCtx" or "is a custom cancelCtx that is overwritten with a done channel"
		// In both cases, the association cannot be established through the children map of the parent Context, and can only be done by creating a Goroutine and unlinking it.
		go func() {
			select {
			case <-parent.Done():
				child.cancel(false, parent.Err())
			case <-child.Done():
			}
		}()
	}
}

// parentCancelCtx returns the underlying *cancelCtx for parent.
// It does this by looking up parent.Value(&cancelCtxKey) to find
// the innermost enclosing *cancelCtx and then checking whether
// parent.Done() matches that *cancelCtx. (If not, the *cancelCtx
// has been wrapped in a custom implementation providing a
// different done channel, in which case we should not bypass it.)
func parentCancelCtx(parent Context) (*cancelCtx, bool) {
	// If the done of the parent context is nil, which means cancel is not supported, then it can't be cancelCtx
	// If `done` of `parent context` is a reusable `closedchan` it means that `parent context` has `canceled`
	// At this point to take out the cancelCtx does not make sense (the specific why no sense will be analyzed later in the chapter))
	done := parent.Done()
	if done == closedchan || done == nil {
		return nil, false
	}

	// If the parent context belongs to the native *cancelCtx or a derived type (timerCtx), you need to continue with the subsequent determination
	// If the parent context cannot be converted to *cancelCtx, it is considered non-cancelCtx and returns nil,fasle
	p, ok := parent.Value(&cancelCtxKey).(*cancelCtx)
	if !ok {
		return nil, false
	}
	p.mu.Lock()
	// After the above determination, the parent context can be converted to *cancelCtx, and there are several cases:
	//   - parent context is `*cancelCtx`
	//   - parent context is timerCtx from the standard library
	//   - parent context is a custom wrapped cancelCtx
	//
	// For these 3 cases need to be judged, the judgment method is: 
	//   Determine whether the done channel obtained by the parent context through the Done() method is the same as the done channel of the context found by Value
	// 
	// Consistency means that the parent context is a cancelCtx or timerCtx or a custom cancelCtx and Done() is not overridden.
	// In this case it can be assumed that the underlying *cancelCtx is obtained
	// 
	// The inconsistency means that the parent context is a custom cancelCtx that overrides the Done() method and does not return the done channel of the standard *cancelCtx, which needs to be handled separately and therefore returns nil, false
	ok = p.done == done
	p.mu.Unlock()
	if !ok {
		return nil, false
	}
	return p, true
}

3.2 How cancelCtx is canceled

As you can see in the cancelCtx creation source code above, the cancelCtx internal signaling across multiple Goroutines actually relies on a done channel; if you want to cancel this Context, then you need to make all <-c.Done() stop blocking, the easiest way is to close this channel directly, or simply replace it with a channel that has already been closed, which is in fact how it is officially done.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// 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) {
    // First determine whether err is nil, if not nil then directly panic
    // The reason for this is that the cancel method is a private method, and any call to cancel in the standard library is guaranteed to pass in err, so if it doesn't, it's an abnormal call, so you can just panic
	if err == nil {
		panic("context: internal error: missing cancel error")
	}
	// Lock on context to prevent concurrent changes
	c.mu.Lock()
	// If there are concurrent accesses after locking, then a second judgment err can prevent repeated cancel calls
	if c.err != nil {
		c.mu.Unlock()
		return // already canceled
	}
	// The internal err is set here, so the above judgment c.err ! = nil corresponds to this
	// That is, after locking there must be a Goroutine cannel first, after cannel c.err must not be nil
	c.err = err
	// Determine whether the internal done channel is nil or not, because the done channel was not initialized immediately when context.WithCancel created the cancelCtx (delayed initialization), so it may be nil here
	// If the done channel is nil, then set it to a shared reusable channel that has already been closed
	if c.done == nil {
		c.done = closedchan
	} else { // If the done channel has already been initialized, close it directly
		close(c.done)
	}
	// If there are associated child Contexts below the current Context, and these child Contexts are all Contexts that can be converted to *cancelCtx (see the propagateCancel method analysis above), then
	// Iterate over the childre map directly and call cancel on the child Context
	// If the associated child Context cannot be converted to *cancelCtx, then a separate Goroutine has been created in the propagateCancel method to close these child Contexts
	for child := range c.children {
		// NOTE: acquiring the child's lock while holding parent's lock.
		child.cancel(false, err)
	}
	// Clear the c.children map and unlock it
	c.children = nil
	c.mu.Unlock()

    // If removeFromParent is true, then clear yourself from the parent Context
	if removeFromParent {
		removeChild(c.Context, c)
	}
}

3.3. parentCancelCtx Why not take out the canceled cancelCtx

In section 3.1 above, when analyzing the parentCancelCtx method, there is this paragraph:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
func parentCancelCtx(parent Context) (*cancelCtx, bool) {
	// If the done of the parent context is nil, which means cancel is not supported, then it can't be cancelCtx
	// If `done` of `parent context` is a reusable `closedchan` it means that `parent context` has `canceled`
	// At this point to take out `cancelCtx` does not make sense (the specific why no sense later chapter will have analysis)
	done := parent.Done()
	if done == closedchan || done == nil {
		return nil, false
	}
	
	// ...... Abbreviation
}

Now to elaborate on the “Why doesn’t it make sense?” The question:

The first is where the parentCancelCtx method is called. There are only two places in the context package where the parentCancelCtx method is called; one is in the func WithCancel(parent Context)that creates thecancelCtx propagateCancel(parent, &c) method, and the other is in the removeChild(c.Context, c) call of the cancel method; here is an analysis of the purpose of these two methods.

3.3.1 propagateCancel(parent, &c)

propagateCancel is responsible for ensuring that the correct parent cancelCtx is passed to the child Context when it is cancelled; then it needs to determine if the parent Context is a parentCancelCtx by parentCancelCtx. cancelCtx, if so then add child Context to children map of parent Context, and then parent Context will automatically traverse the map to call child Context’s cancel; if not, then open Goroutine to block reading parent Context’s done channel and then call child Context’s cancel.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
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 {
    atomic.AddInt32(&goroutines, +1)
    go func() {
        select {
        case <-parent.Done():
            child.cancel(false, parent.Err())
        case <-child.Done():
        }
    }()
}

So in this method call, if parentCancelCtx fetches a cancelled cancelCtx, then the children map of parent Context has been cleared at the time of cancel, so there is a problem if it is set again. propagateCancel is to control the propagation, obviously parent Context has already cancel up, then it is meaningless to propagate

3.3.2. removeChild(c.Context, c)

As in 3.3.1 above, the purpose of removeChild(c.Context, c) is to disconnect from parent Context when cancel, also to deal with the children map problem; if parentCancelCtx also takes out a parent Context that has been canceled, there is no point in trying to remove again since the parent Context has already been cleared of the childre map at the time of cancel.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// 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()
}

4. timerCtx source code analysis

4.1. How timerCtx is created

timerCtx is created mainly through the context.WithDeadline method, while context.WithTimeout is actually called from context:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// 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) {
    // As with cancelCtx, first check the parent Context
	if parent == nil {
		panic("cannot create context from nil parent")
	}
    
    // Determine whether the parent Context supports Deadline, if so, you need to determine the cutoff time of the parent Context
    // Assuming that the expiration time of the parent Context is earlier than the currently set expiration time, it means that the parent Context will definitely be cancelled first, and the current child Context will also be cancelled due to the parent Context's cancellation
    // So this time directly return a cancelCtx on the line, the timer has no need to exist
	if cur, ok := parent.Deadline(); ok && cur.Before(d) {
		// The current deadline is already sooner than the new one.
		return WithCancel(parent)
	}

    // Create a timerCtx
	c := &timerCtx{
		cancelCtx: newCancelCtx(parent),
		deadline:  d,
	}

    // The same propagation operation as cancelCtx
	propagateCancel(parent, c)

    // Determine if the current time has passed the deadline, if it exceeds the deadline directly cancel
	dur := time.Until(d)
	if dur <= 0 {
		c.cancel(true, DeadlineExceeded) // deadline has already passed
		return c, func() { c.cancel(false, Canceled) }
	}

    // If all checks are OK, create a timer that will automatically cancel when the time is up
	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) }
}

4.2. How timerCtx is cancelled

After understanding the cancellation process of cancelCtx, the cancellation of timerCtx is relatively simple, it is mainly a call to cancelCtx’s cancel and then stop the timer:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
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()
}

5. valueCtx source code analysis

Compared to cancelCtx and timerCtx, valueCtx is really too simple because it has no concatenated cancellation logic and no overly complex kv storage:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
// 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 {
    // parent Detection
	if parent == nil {
		panic("cannot create context from nil parent")
	}
    // key Detection
	if key == nil {
		panic("nil key")
	}
    // key must be comparable
	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{} {
    // First, determine whether the key is present in the current Context
	if c.key == key {
		return c.val
	}
    // If there is no recursive upward lookup
	return c.Context.Value(key)
}

6. Ending

Analysis of the Context source code has been intermittently experienced 3, 4 days, to say the heart found that there are many complications, many other articles on the Internet are only mentioned a mouth, but not in-depth specific logic, especially the call related to cancelCtx; I even think I may not understand some places are not completely correct, for the time being to write here, if there is not the right place welcome to add.


Reference https://mritd.com/2021/06/27/golang-context-source-code/