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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
|
// 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 outgoingname
// 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.
//
// The WithCancelCause function returns a CancelCauseFunc, which
// takes an error and records it as the cancellation cause. Calling
// Cause on the canceled context or any of its children retrieves
// the cause. If no cause is specified, Cause(ctx) returns the same
// value as ctx.Err().
//
// 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"
"sync/atomic"
"time"
"fmt"
)
// 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的bool值,
// 如果没有设置deadline,则 ok == false,此时 deadline 为一个初始值的time.Time值
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.
// The close of the Done channel may happen asynchronously,
// after the cancel function returns.
//
// 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.
// 该方法返回一个 channel,需要在 select-case 语句中使用,如” case <-context.Done():”。
// 当context关闭后,Done()返回一个被关闭的 chan,关闭的管道仍然是可读的,据此goroutine可以收到关闭请求;
// 当context还未关闭时,Done()返回 nil。
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
// }
// valueCtx 不是用于控制呈树状分布的goroutine,而是用于在树状分布的goroutine间传递信息。
// Value()方法就是用于此种类型的context,该方法根据key值查询map中的value。
Value(key any) any
}
// 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.
// 用于context的根节点,空的context只是简单的实现了Context,
// 本身不包含任何值,仅用于其他context的父节点。
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 any) any {
return nil
}
func (e *emptyCtx) String() string {
switch e {
case background:
return "context.Background"
case todo:
return "context.TODO"
}
return "unknown empty Context"
}
// context包提供了4个方法创建不同类型的context,
// 使用这四个方法时如果没有父context,都需要传入emptyCtx ,即backgroud或todo作为其父节点:
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.
// A CancelFunc may be called by multiple goroutines simultaneously.
// 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.
// 函数接受一个父上下文对象parent 作为参数,返回一个新的上下文cancelCtx对象ctx 及其对应的取消函数cancel 。
// 当调用取消函数时,将通过 context 的取消信号来通知这个上下文相关联的所有操作停止执行并释放资源。
//
func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
c := withCancel(parent)
// 将构造的cancelCtx返回,同时返回终止该cancelCtx的闭包函数cancel
// 第一个参数是 true,也就是说取消的时候,需要将自己从父节点里删除。第二个参数则是一个固定的取消错误类型
return c, func() { c.cancel(true, Canceled, nil) }
}
// A CancelCauseFunc behaves like a CancelFunc but additionally sets the cancellation cause.
// This cause can be retrieved by calling Cause on the canceled Context or on
// any of its derived Contexts.
//
// If the context has already been canceled, CancelCauseFunc does not set the cause.
// For example, if childContext is derived from parentContext:
// - if parentContext is canceled with cause1 before childContext is canceled with cause2,
// then Cause(parentContext) == Cause(childContext) == cause1
// - if childContext is canceled with cause2 before parentContext is canceled with cause1,
// then Cause(parentContext) == cause1 and Cause(childContext) == cause2
type CancelCauseFunc func(cause error)
// WithCancelCause behaves like WithCancel but returns a CancelCauseFunc instead of a CancelFunc.
// Calling cancel with a non-nil error (the "cause") records that error in ctx;
// it can then be retrieved using Cause(ctx).
// Calling cancel with nil sets the cause to Canceled.
//
// Example use:
//
// ctx, cancel := context.WithCancelCause(parent)
// cancel(myError)
// ctx.Err() // returns context.Canceled
// context.Cause(ctx) // returns myError
func WithCancelCause(parent Context) (ctx Context, cancel CancelCauseFunc) {
c := withCancel(parent)
return c, func(cause error) { c.cancel(true, Canceled, cause) }
}
// 1. 如果 parent 传入的是 background 或 todo 对象对象,在调用 propagateCancel 时什么也不做,
// 直接使用 parent 创建一个 *cancelCtx 对象返回;
// 2. 如果传入的是一个已经被 cancel 的 parent,在调用 propagateCancel 时直接把新创建的 cancle 掉;
// 3. 如果传入的是 *cancelCtx,*timerCtx 等类型的对象,则使用 parent 创建一个 *cancelCtx 对象后,
// 在调用 propagateCancel 时还要把新创建的 *cancelCtx 对象 放入到 parent 的 children 集合中;
func withCancel(parent Context) *cancelCtx {
if parent == nil {
panic("cannot create context from nil parent")
}
c := newCancelCtx(parent) // 注入parent——父context构造cancelCtx;
// propagateCancel方法内启动一个守护协程,当父context终止时,该cancelCtx 也被终止;
propagateCancel(parent, c)
return c
}
// Cause returns a non-nil error explaining why c was canceled.
// The first cancellation of c or one of its parents sets the cause.
// If that cancellation happened via a call to CancelCauseFunc(err),
// then Cause returns err.
// Otherwise Cause(c) returns the same value as c.Err().
// Cause returns nil if c has not been canceled yet.
func Cause(c Context) error {
if cc, ok := c.Value(&cancelCtxKey).(*cancelCtx); ok {
cc.mu.Lock()
defer cc.mu.Unlock()
return cc.cause
}
return nil
}
// newCancelCtx returns an initialized cancelCtx.
// 注入parent 父 context 后,返回一个新构造的的 cancelCtx.
func newCancelCtx(parent Context) *cancelCtx {
return &cancelCtx{Context: parent}
}
// goroutines counts the number of goroutines ever created; for testing.
var goroutines atomic.Int32
// propagateCancel arranges for child to be canceled when parent is.
// propagateCancel 用以传递父子 context 之间的 cancel 事件,向上寻找可以“挂靠”的“可取消”的 context,
// 并且“挂靠”上去。这样,调用上层 cancel 方法的时候,就可以层层传递,将那些挂靠的子 context 同时“取消”。
// 1. 如果 parent 是 background 或 todo 对象,函数什么都不做,直接返回;
// 2. 如果传入的是一个已经被 cancel 的 parent,直接把 child 对象 cancle 掉就返回;
// 3. 如果传入的是 *cancelCtx 类型及其派生类型的 parent,则把 child 对象 放入到 parent 的 children 集合中,再返回;
func propagateCancel(parent Context, child canceler) { // parent即父协程,child即当前协程
// 如果 parent 是 background 或 todo 对象,函数什么都不做,直接返回
done := parent.Done()
if done == nil {
// parent 是不会被 cancel 的类型(如 emptyCtx),则直接返回
return // parent is never canceled
}
select {
case <-done:
// parent is already canceled
// parent 已经被 cancel,则直接终止子 context,并以 parent 的 err 作为子 context 的 err
child.cancel(false, parent.Err(), Cause(parent))
return
default:
// parent 未被 cancel,继续下面操作
}
// parentCancelCtx通过 parent.Value(&cancelCtxKey)判断是否是cancelCtx 类型
// 倘若以特定的 cancelCtxKey 从 parent 中通过parent.Value()取值,取得的 value 是 parent 本身,则返回 true.
// 倘若 parent 的 channel 已关闭或者是不会被 cancel 的类型,则返回 false;
if p, ok := parentCancelCtx(parent); ok {
// parent 是 *cancelCtx 类型
fmt.Printf("children0: %#v\n", p.children)
p.mu.Lock()
if p.err != nil {
// parent has already been canceled
child.cancel(false, p.err, p.cause)
} else {
if p.children == nil {
p.children = make(map[canceler]struct{})
}
// 把新创建的 child 存入 parent 的 children 集合中
p.children[child] = struct{}{}
}
p.mu.Unlock()
fmt.Printf("children01: %#v\n", p.children)
} else {
// parent 不是 *cancelCtx 类型,
// 本函数开头已经判断过 parent 也不是 已经被 cancel取消 或者 不会被 cancel 的类型(如 emptyCtx)
// 走到这里,说明 parent 不是 cancelCtx 类型,但又存在 cancel 的能力(比如用户自定义实现的 context),
// 则启动一个协程,通过多路复用的方式监控 parent 状态,倘若其终止,则同时终止子 context,并传递 parent 的 err
goroutines.Add(1)
go func() {
select {
case <-parent.Done():
child.cancel(false, parent.Err(), Cause(parent))
case <-child.Done():
}
}()
}
}
// &cancelCtxKey is the key that a cancelCtx returns itself for.
// 如果使用 &cancelCtxKey 作为 key 调用 cancelCtx 的 Value 方法,将返回调用者自己
var cancelCtxKey int
// 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.)
// parentCancelCtx通过 parent.Value(&cancelCtxKey)判断是否是cancelCtx 类型
// 倘若以特定的 cancelCtxKey 从 parent 中通过parent.Value() 取值,取得的 value 是 parent 本身,则返回 true.
// 倘若 parent 的 channel 已关闭或者是不会被 cancel 的类型,则返回 false;
func parentCancelCtx(parent Context) (*cancelCtx, bool) {
// 判断 parent 若是已经被 cancel取消 或者 不会被 cancel 的类型(如 emptyCtx),则直接返回
// 1. 如果 parent 是 background 或 todo 对象,函数直接返回 nil 和 false
// 2. 如果 parent 已经被 cancel,函数也直接返回 nil 和 false
done := parent.Done()
if done == closedchan || done == nil {
return nil, false
}
// 如果使用 &cancelCtxKey 作为 key 调用 Value 方法,将返回调用者自己
// 如果 parent 是 *cancelCtx 类型对象,则返回它自己,
// 如果 parent 是 cancelCtx 的派生类型对象,则返回 派生类对象 的 *cancelCtx 部分
p, ok := parent.Value(&cancelCtxKey).(*cancelCtx) // 类型断言,如果类型不符合,ok == false
if !ok {
return nil, false // 如果 parent 的类型不是 *cancelCtx 返回 nil 和 false
}
// 如果 parent 的类型是 *cancelCtx 及其派生类对象,判断 done 相同则 返回 p
pdone, _ := p.done.Load().(chan struct{})
if pdone != done {
return nil, false
}
return p, true
}
// removeChild removes a context from its parent.
func removeChild(parent Context, child canceler) {
p, ok := parentCancelCtx(parent)
if !ok {
// parent 不是 *cancelCtx 类型,直接返回
return
}
// parent 是 *cancelCtx 类型,从 p.children 中删除 child
p.mu.Lock()
fmt.Printf("children4: %#v\n", p.children)
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.
// 实现了 canceler 接口定义的两个方法的 Context 对象,是可取消的 对象。
// 有两个类型实现了 canceler 接口:*cancelCtx 和 *timerCtx。
// 注意是加了 * 号的,是这两个结构体内部的指针实现了 canceler 接口。
// 只有实现了 canceler 的 对象才能存在 children 中
type canceler interface {
cancel(removeFromParent bool, err, cause 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.
// 这是一个可以取消的 Context,实现了 canceler 接口。
// 它直接将接口 Context 作为它的一个匿名字段,这样,它就可以被看成一个 Context。
type cancelCtx struct {
// 嵌入式接口类型,cancelCtx 必然为某个 context 的子 context;
Context
mu sync.Mutex // protects following fields
// 原子通道,第一次调用取消函数时被惰性创建,在该 context 及其后代 context 都被取消时关闭
done atomic.Value // of chan struct{}, created lazily, closed by first cancel call
// 值为struct{}其实是一个set,保存当前上下文的所有子上下文,第一次取消调用时设为 nil
// 只有实现了 canceler 的对象才能记录在 children 中
children map[canceler]struct{} // set to nil by the first cancel call
// 第一次取消操作时设置为一个错误值,对此上下文及后代上下文进行取消操作返回该错误
err error // set to non-nil by the first cancel call
cause error // set to non-nil by the first cancel call
}
// 调用函数时 若接收者是 cancelCtx 的派生类对象,则在方法体中接收者 变量 c 将会做类型转换为 cancelCtx 类型
func (c *cancelCtx) Value(key any) any {
// 倘若传入的 key 等于特定值 &cancelCtxKey,则返回 cancelCtx 自身的指针;
// 基于 cancelCtxKey 为 key 取值时返回 cancelCtx 自身,是 cancelCtx 特有的协议
if key == &cancelCtxKey {
return c
}
// 否则就遵循 valueCtx 的思路取值返回
return value(c.Context, key)
}
// c.done 使用了惰性加载(lazy loading)的机制,只有第一次调用 Done() 方法的时候 通道 才会被创建,
// 且通过加锁二次检查,确保在多个goroutine同时调用 Done() 方法时,只有第一个 goroutine 创建通道,
// 其它 goroutine 均复用已创建的通道。函数返回的是一个只读的 channel,
// 一般通过搭配 select 来使用,当channel关闭后,就会立即读出零值,据此可以判断cancelCtx是否被取消。
func (c *cancelCtx) Done() <-chan struct{} {
// 基于 atomic 包,读取 cancelCtx 中的 chan;倘若已存在,则直接返回;
d := c.done.Load()
if d != nil {
return d.(chan struct{})
}
// 加锁后,再次检查 chan 是否存在,若存在则返回;(double check)
c.mu.Lock()
defer c.mu.Unlock()
d = c.done.Load()
if d == nil {
d = make(chan struct{})
// 初始化 chan 存储到 aotmic.Value 当中,并返回.(懒加载机制)
c.done.Store(d)
}
return d.(chan struct{})
}
// cancelCtx.err默认是nil,在context被cancel时指定一个error变量: “context canceled”。
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.
// cancel sets c.cause to cause if this is the first time c is canceled.
// cancelCtx.cancel 方法有两个入参,第一个 removeFromParent 是一个 bool 值,
// 表示当前 context 是否需要从父 context 的 children set 中删除;
// 第二个 err 则是 cancel 后需要展示的错误;
// 调用函数会递归的 cancel 掉 cancelCtx 自己 和 cancelCtx 派生出来的 子 Context(canelCtx、timerCtx 等等类型的 Context)
func (c *cancelCtx) cancel(removeFromParent bool, err, cause error) {
// 判断err参数是否为空,
// 若为空则引发panic(“context: internal error: missing cancel error”)
if err == nil {
panic("context: internal error: missing cancel error")
}
if cause == nil {
cause = err
}
// 首先加锁
c.mu.Lock()
// 判断cancelCtx 自带的 err 是否已经非空,若非空说明cancelCtx 已被 cancel,则解锁返回
if c.err != nil {
c.mu.Unlock()
return // already canceled
}
// 首先 设置自己的 err 和 关闭自己的 done 管道
c.err = err
c.cause = cause
d, _ := c.done.Load().(chan struct{})
// 判断channel是否初始化,
if d == nil {
// c.done 若未初始化,则直接注入一个 closedChan 关闭的通道,
c.done.Store(closedchan)
} else {
// 否则关闭已有的channel
close(d)
}
fmt.Printf("children1: %#v\n", c.children)
// 然后遍历所有 由自己派生出来的 子 canceler 对象,并调用每个子 canceler 对象 各自的 cancel 函数
//
for child := range c.children {
// NOTE: acquiring the child's lock while holding parent's lock.
// 递归的 cancel
child.cancel(false, err, cause)
}
// 置空(nil)children, 这将把自己 与 所有的 派生子对象 断开联系,子对象将被 GC
c.children = nil
c.mu.Unlock() // 解锁
// 根据传入的 removeFromParent flag 判断是否需要手动把 自己 从 c.Context 指针指向的对象(parent)的 children set 中delete 删除
// 自己将被 GC
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 parent == nil {
panic("cannot create context from nil parent")
}
//校验 parent是否可过期, 且过期时间是否早于自己,若是,则构造一个 cancelCtx 返回即可;
// 如果 parent(是 timerCtx 或 timerCtx对象 派生的子对象)调用 Deadline 成功 返回 自己的截至时间,
// 并且这个时间早于 新设置指定的截止时间 d,则 子Context 对象无需创建为 timerCtx ,
// 直接创建一个 cancelCtx 的 子 Context对象 返回即可,
// 这可以省去子Context 的 计时器;
// 这是因为即使 创建了 timerCtx 的 子 Context 对象 并设置了超时时间 d,
// 这个子对象也会在自己超时前,被 parent 对象 cancel掉(可能是 parent 超时 或者 调用 cancel)
// 因此、这里设置计时器将不起作用,和不设置效果一样
if cur, ok := parent.Deadline(); ok && cur.Before(d) {
// The current deadline is already sooner than the new one.
return WithCancel(parent)
}
// 创建 timerCtx 指针
c := &timerCtx{
cancelCtx: newCancelCtx(parent), // 直接 new 一个新的 cancelCtx 指针
deadline: d, // 记录过期时间
}
// 1. 如果 parent 是 background 或 todo 对象,propagateCancel函数什么都不做,直接返回;
// 2. 如果传入propagateCancel的 parent 是一个已经被 cancel 的 parent,直接把 c 对象 cancle 掉就返回;
// 3. 如果传入propagateCancel的 parent 是 *cancelCtx 类型或其派生类型 的 parent,则把 c 对象 放入到 parent 的 children 集合中,再返回;
propagateCancel(parent, c)
dur := time.Until(d)
if dur <= 0 {
// 如果已经超时了, 就直接把 新创建的 c 取消掉
c.cancel(true, DeadlineExceeded, nil) // deadline has already passed
return c, func() { c.cancel(false, Canceled, nil) }
}
//加锁; c.cancel() 和 time.AfterFunc() 两个操作的原子性和同步性确保在计时器触发前或在取消方法执行后不再触发计时器。
c.mu.Lock()
defer c.mu.Unlock()
if c.err == nil {
// 如果c 还未被取消,则设置 计时器 及 超时回调函数(取消 c 操作)
// 启动time.Timer,达到过期时间后会调用cancel终止该 timerCtx,并返回 DeadlineExceeded 的错误;
// time.AfterFunc(dur, func()) 函数的工作方式是异步的,它创建一个新的 Goroutine 来运行计时器并等待计时器触发。
c.timer = time.AfterFunc(dur, func() {
c.cancel(true, DeadlineExceeded, nil)
})
}
return c, func() { c.cancel(true, Canceled, nil) }
}
// 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.
// timerCtx 在 cancelCtx 基础上又做了一层封装,除了继承 cancelCtx 的能力之外,
// 新增了一个 time.Timer 用于定时终止 context;
// 另外新增了一个 deadline 字段用于存放 timerCtx 的过期时间.
// 由于 timerCtx 继承了 cancelCtx 结构体,timerCtx 可以从其父亲结构体获得取消能力,
// 同时也可以使用它的成员变量 timer 来设置超时。
// 当计时器触发时(超时),会使用与该上下文相关联的取消函数来取消该上下文中运行的所有操作。
type timerCtx struct {
*cancelCtx // 嵌入式 cancelCtx 结构体指针,继承了 cancelCtx 中的所有字段和方法
timer *time.Timer
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, cause error) {
// 调用匿名内嵌成员 cancelCtx 的 cancel 取消 自己 和 自己派生的所有的 children(如果有children)
// 取消时不用删除 children 中的 子 对象,只会递归调用 cancel 设置 err 并 关闭 done 管道
// 这将使得 自己和所有的子 对象 都被设置 err 和 关闭 done 管道
c.cancelCtx.cancel(false, err, cause)
// 执行完 cancel 后,再把自己 从 parent 的 children 中 delete 掉,
// 这将导致 自己 和 所有的 子对象 被 GC
if removeFromParent {
// Remove this timerCtx from its parent cancelCtx's children.
removeChild(c.cancelCtx.Context, c)
}
c.mu.Lock()
// 最后再 停掉计时器,并置空,使计时器 被 GC
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 any) Context {
if parent == nil {
panic("cannot create context from nil parent")
}
if key == nil {
panic("nil key")
}
if !reflectlite.TypeOf(key).Comparable() { // 若 key 的类型不可比较,panic;
panic("key is not comparable")
}
// 使用 parent context 以及 kv对 构造 一个新的 valueCtx 对象 并返回
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 any
}
// 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 any) 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 any) any {
if c.key == key {
return c.val
}
// c 当前对象中查找不到key, 就调用 value 函数 去 其 Context(parent)成员中查找
return value(c.Context, key)
}
func value(c Context, key any) any {
// 从 当前 Context 层 递归的往上层 逐层查找,直到找到为止,找到后返回key对现有的 value,
// 或找到根节点(emptyCtx)层还没找到则返回 nil
// 下面的 for 循环 结合 default 语句的 c.Value(key) 方法中 对 value 函数调用闭合形成递归调用
for {
switch ctx := c.(type) {
case *valueCtx:
if key == ctx.key {
return ctx.val
}
c = ctx.Context
case *cancelCtx:
if key == &cancelCtxKey {
return c
}
c = ctx.Context
case *timerCtx:
if key == &cancelCtxKey {
return ctx.cancelCtx
}
c = ctx.Context
case *emptyCtx:
return nil
default:
return c.Value(key) // c.Value(key) 方法中 对 本函数 value 的调用闭合形成递归调用
}
}
}
|