Source file
src/context/context_test.go
1
2
3
4
5 package context
6
7 import (
8 "fmt"
9 "math/rand"
10 "runtime"
11 "strings"
12 "sync"
13 "sync/atomic"
14 "time"
15 )
16
17 type testingT interface {
18 Deadline() (time.Time, bool)
19 Error(args ...any)
20 Errorf(format string, args ...any)
21 Fail()
22 FailNow()
23 Failed() bool
24 Fatal(args ...any)
25 Fatalf(format string, args ...any)
26 Helper()
27 Log(args ...any)
28 Logf(format string, args ...any)
29 Name() string
30 Parallel()
31 Skip(args ...any)
32 SkipNow()
33 Skipf(format string, args ...any)
34 Skipped() bool
35 }
36
37
38
39
40 type otherContext struct {
41 Context
42 }
43
44 const (
45 shortDuration = 1 * time.Millisecond
46 veryLongDuration = 1000 * time.Hour
47 )
48
49
50
51 func quiescent(t testingT) time.Duration {
52 deadline, ok := t.Deadline()
53 if !ok {
54 return 5 * time.Second
55 }
56
57 const arbitraryCleanupMargin = 1 * time.Second
58 return time.Until(deadline) - arbitraryCleanupMargin
59 }
60
61 func XTestBackground(t testingT) {
62 c := Background()
63 if c == nil {
64 t.Fatalf("Background returned nil")
65 }
66 select {
67 case x := <-c.Done():
68 t.Errorf("<-c.Done() == %v want nothing (it should block)", x)
69 default:
70 }
71 if got, want := fmt.Sprint(c), "context.Background"; got != want {
72 t.Errorf("Background().String() = %q want %q", got, want)
73 }
74 }
75
76 func XTestTODO(t testingT) {
77 c := TODO()
78 if c == nil {
79 t.Fatalf("TODO returned nil")
80 }
81 select {
82 case x := <-c.Done():
83 t.Errorf("<-c.Done() == %v want nothing (it should block)", x)
84 default:
85 }
86 if got, want := fmt.Sprint(c), "context.TODO"; got != want {
87 t.Errorf("TODO().String() = %q want %q", got, want)
88 }
89 }
90
91 func XTestWithCancel(t testingT) {
92 c1, cancel := WithCancel(Background())
93
94 if got, want := fmt.Sprint(c1), "context.Background.WithCancel"; got != want {
95 t.Errorf("c1.String() = %q want %q", got, want)
96 }
97
98 o := otherContext{c1}
99 c2, _ := WithCancel(o)
100 contexts := []Context{c1, o, c2}
101
102 for i, c := range contexts {
103 if d := c.Done(); d == nil {
104 t.Errorf("c[%d].Done() == %v want non-nil", i, d)
105 }
106 if e := c.Err(); e != nil {
107 t.Errorf("c[%d].Err() == %v want nil", i, e)
108 }
109
110 select {
111 case x := <-c.Done():
112 t.Errorf("<-c.Done() == %v want nothing (it should block)", x)
113 default:
114 }
115 }
116
117 cancel()
118 for i, c := range contexts {
119 select {
120 case <-c.Done():
121 default:
122 t.Errorf("<-c[%d].Done() blocked, but shouldn't have", i)
123 }
124 if e := c.Err(); e != Canceled {
125 t.Errorf("c[%d].Err() == %v want %v", i, e, Canceled)
126 }
127 }
128 }
129
130 func contains(m map[canceler]struct{}, key canceler) bool {
131 _, ret := m[key]
132 return ret
133 }
134
135 func XTestParentFinishesChild(t testingT) {
136
137
138
139 parent, cancel := WithCancel(Background())
140 cancelChild, stop := WithCancel(parent)
141 defer stop()
142 valueChild := WithValue(parent, "key", "value")
143 timerChild, stop := WithTimeout(valueChild, veryLongDuration)
144 defer stop()
145
146 select {
147 case x := <-parent.Done():
148 t.Errorf("<-parent.Done() == %v want nothing (it should block)", x)
149 case x := <-cancelChild.Done():
150 t.Errorf("<-cancelChild.Done() == %v want nothing (it should block)", x)
151 case x := <-timerChild.Done():
152 t.Errorf("<-timerChild.Done() == %v want nothing (it should block)", x)
153 case x := <-valueChild.Done():
154 t.Errorf("<-valueChild.Done() == %v want nothing (it should block)", x)
155 default:
156 }
157
158
159 pc := parent.(*cancelCtx)
160 cc := cancelChild.(*cancelCtx)
161 tc := timerChild.(*timerCtx)
162 pc.mu.Lock()
163 if len(pc.children) != 2 || !contains(pc.children, cc) || !contains(pc.children, tc) {
164 t.Errorf("bad linkage: pc.children = %v, want %v and %v",
165 pc.children, cc, tc)
166 }
167 pc.mu.Unlock()
168
169 if p, ok := parentCancelCtx(cc.Context); !ok || p != pc {
170 t.Errorf("bad linkage: parentCancelCtx(cancelChild.Context) = %v, %v want %v, true", p, ok, pc)
171 }
172 if p, ok := parentCancelCtx(tc.Context); !ok || p != pc {
173 t.Errorf("bad linkage: parentCancelCtx(timerChild.Context) = %v, %v want %v, true", p, ok, pc)
174 }
175
176 cancel()
177
178 pc.mu.Lock()
179 if len(pc.children) != 0 {
180 t.Errorf("pc.cancel didn't clear pc.children = %v", pc.children)
181 }
182 pc.mu.Unlock()
183
184
185 check := func(ctx Context, name string) {
186 select {
187 case <-ctx.Done():
188 default:
189 t.Errorf("<-%s.Done() blocked, but shouldn't have", name)
190 }
191 if e := ctx.Err(); e != Canceled {
192 t.Errorf("%s.Err() == %v want %v", name, e, Canceled)
193 }
194 }
195 check(parent, "parent")
196 check(cancelChild, "cancelChild")
197 check(valueChild, "valueChild")
198 check(timerChild, "timerChild")
199
200
201 precanceledChild := WithValue(parent, "key", "value")
202 select {
203 case <-precanceledChild.Done():
204 default:
205 t.Errorf("<-precanceledChild.Done() blocked, but shouldn't have")
206 }
207 if e := precanceledChild.Err(); e != Canceled {
208 t.Errorf("precanceledChild.Err() == %v want %v", e, Canceled)
209 }
210 }
211
212 func XTestChildFinishesFirst(t testingT) {
213 cancelable, stop := WithCancel(Background())
214 defer stop()
215 for _, parent := range []Context{Background(), cancelable} {
216 child, cancel := WithCancel(parent)
217
218 select {
219 case x := <-parent.Done():
220 t.Errorf("<-parent.Done() == %v want nothing (it should block)", x)
221 case x := <-child.Done():
222 t.Errorf("<-child.Done() == %v want nothing (it should block)", x)
223 default:
224 }
225
226 cc := child.(*cancelCtx)
227 pc, pcok := parent.(*cancelCtx)
228 if p, ok := parentCancelCtx(cc.Context); ok != pcok || (ok && pc != p) {
229 t.Errorf("bad linkage: parentCancelCtx(cc.Context) = %v, %v want %v, %v", p, ok, pc, pcok)
230 }
231
232 if pcok {
233 pc.mu.Lock()
234 if len(pc.children) != 1 || !contains(pc.children, cc) {
235 t.Errorf("bad linkage: pc.children = %v, cc = %v", pc.children, cc)
236 }
237 pc.mu.Unlock()
238 }
239
240 cancel()
241
242 if pcok {
243 pc.mu.Lock()
244 if len(pc.children) != 0 {
245 t.Errorf("child's cancel didn't remove self from pc.children = %v", pc.children)
246 }
247 pc.mu.Unlock()
248 }
249
250
251 select {
252 case <-child.Done():
253 default:
254 t.Errorf("<-child.Done() blocked, but shouldn't have")
255 }
256 if e := child.Err(); e != Canceled {
257 t.Errorf("child.Err() == %v want %v", e, Canceled)
258 }
259
260
261 select {
262 case x := <-parent.Done():
263 t.Errorf("<-parent.Done() == %v want nothing (it should block)", x)
264 default:
265 }
266 if e := parent.Err(); e != nil {
267 t.Errorf("parent.Err() == %v want nil", e)
268 }
269 }
270 }
271
272 func testDeadline(c Context, name string, t testingT) {
273 t.Helper()
274 d := quiescent(t)
275 timer := time.NewTimer(d)
276 defer timer.Stop()
277 select {
278 case <-timer.C:
279 t.Fatalf("%s: context not timed out after %v", name, d)
280 case <-c.Done():
281 }
282 if e := c.Err(); e != DeadlineExceeded {
283 t.Errorf("%s: c.Err() == %v; want %v", name, e, DeadlineExceeded)
284 }
285 }
286
287 func XTestDeadline(t testingT) {
288 t.Parallel()
289
290 c, _ := WithDeadline(Background(), time.Now().Add(shortDuration))
291 if got, prefix := fmt.Sprint(c), "context.Background.WithDeadline("; !strings.HasPrefix(got, prefix) {
292 t.Errorf("c.String() = %q want prefix %q", got, prefix)
293 }
294 testDeadline(c, "WithDeadline", t)
295
296 c, _ = WithDeadline(Background(), time.Now().Add(shortDuration))
297 o := otherContext{c}
298 testDeadline(o, "WithDeadline+otherContext", t)
299
300 c, _ = WithDeadline(Background(), time.Now().Add(shortDuration))
301 o = otherContext{c}
302 c, _ = WithDeadline(o, time.Now().Add(veryLongDuration))
303 testDeadline(c, "WithDeadline+otherContext+WithDeadline", t)
304
305 c, _ = WithDeadline(Background(), time.Now().Add(-shortDuration))
306 testDeadline(c, "WithDeadline+inthepast", t)
307
308 c, _ = WithDeadline(Background(), time.Now())
309 testDeadline(c, "WithDeadline+now", t)
310 }
311
312 func XTestTimeout(t testingT) {
313 t.Parallel()
314
315 c, _ := WithTimeout(Background(), shortDuration)
316 if got, prefix := fmt.Sprint(c), "context.Background.WithDeadline("; !strings.HasPrefix(got, prefix) {
317 t.Errorf("c.String() = %q want prefix %q", got, prefix)
318 }
319 testDeadline(c, "WithTimeout", t)
320
321 c, _ = WithTimeout(Background(), shortDuration)
322 o := otherContext{c}
323 testDeadline(o, "WithTimeout+otherContext", t)
324
325 c, _ = WithTimeout(Background(), shortDuration)
326 o = otherContext{c}
327 c, _ = WithTimeout(o, veryLongDuration)
328 testDeadline(c, "WithTimeout+otherContext+WithTimeout", t)
329 }
330
331 func XTestCanceledTimeout(t testingT) {
332 c, _ := WithTimeout(Background(), time.Second)
333 o := otherContext{c}
334 c, cancel := WithTimeout(o, veryLongDuration)
335 cancel()
336 select {
337 case <-c.Done():
338 default:
339 t.Errorf("<-c.Done() blocked, but shouldn't have")
340 }
341 if e := c.Err(); e != Canceled {
342 t.Errorf("c.Err() == %v want %v", e, Canceled)
343 }
344 }
345
346 type key1 int
347 type key2 int
348
349 var k1 = key1(1)
350 var k2 = key2(1)
351 var k3 = key2(3)
352
353 func XTestValues(t testingT) {
354 check := func(c Context, nm, v1, v2, v3 string) {
355 if v, ok := c.Value(k1).(string); ok == (len(v1) == 0) || v != v1 {
356 t.Errorf(`%s.Value(k1).(string) = %q, %t want %q, %t`, nm, v, ok, v1, len(v1) != 0)
357 }
358 if v, ok := c.Value(k2).(string); ok == (len(v2) == 0) || v != v2 {
359 t.Errorf(`%s.Value(k2).(string) = %q, %t want %q, %t`, nm, v, ok, v2, len(v2) != 0)
360 }
361 if v, ok := c.Value(k3).(string); ok == (len(v3) == 0) || v != v3 {
362 t.Errorf(`%s.Value(k3).(string) = %q, %t want %q, %t`, nm, v, ok, v3, len(v3) != 0)
363 }
364 }
365
366 c0 := Background()
367 check(c0, "c0", "", "", "")
368
369 c1 := WithValue(Background(), k1, "c1k1")
370 check(c1, "c1", "c1k1", "", "")
371
372 if got, want := fmt.Sprint(c1), `context.Background.WithValue(type context.key1, val c1k1)`; got != want {
373 t.Errorf("c.String() = %q want %q", got, want)
374 }
375
376 c2 := WithValue(c1, k2, "c2k2")
377 check(c2, "c2", "c1k1", "c2k2", "")
378
379 c3 := WithValue(c2, k3, "c3k3")
380 check(c3, "c2", "c1k1", "c2k2", "c3k3")
381
382 c4 := WithValue(c3, k1, nil)
383 check(c4, "c4", "", "c2k2", "c3k3")
384
385 o0 := otherContext{Background()}
386 check(o0, "o0", "", "", "")
387
388 o1 := otherContext{WithValue(Background(), k1, "c1k1")}
389 check(o1, "o1", "c1k1", "", "")
390
391 o2 := WithValue(o1, k2, "o2k2")
392 check(o2, "o2", "c1k1", "o2k2", "")
393
394 o3 := otherContext{c4}
395 check(o3, "o3", "", "c2k2", "c3k3")
396
397 o4 := WithValue(o3, k3, nil)
398 check(o4, "o4", "", "c2k2", "")
399 }
400
401 func XTestAllocs(t testingT, testingShort func() bool, testingAllocsPerRun func(int, func()) float64) {
402 bg := Background()
403 for _, test := range []struct {
404 desc string
405 f func()
406 limit float64
407 gccgoLimit float64
408 }{
409 {
410 desc: "Background()",
411 f: func() { Background() },
412 limit: 0,
413 gccgoLimit: 0,
414 },
415 {
416 desc: fmt.Sprintf("WithValue(bg, %v, nil)", k1),
417 f: func() {
418 c := WithValue(bg, k1, nil)
419 c.Value(k1)
420 },
421 limit: 3,
422 gccgoLimit: 3,
423 },
424 {
425 desc: "WithTimeout(bg, 1*time.Nanosecond)",
426 f: func() {
427 c, _ := WithTimeout(bg, 1*time.Nanosecond)
428 <-c.Done()
429 },
430 limit: 12,
431 gccgoLimit: 15,
432 },
433 {
434 desc: "WithCancel(bg)",
435 f: func() {
436 c, cancel := WithCancel(bg)
437 cancel()
438 <-c.Done()
439 },
440 limit: 5,
441 gccgoLimit: 8,
442 },
443 {
444 desc: "WithTimeout(bg, 5*time.Millisecond)",
445 f: func() {
446 c, cancel := WithTimeout(bg, 5*time.Millisecond)
447 cancel()
448 <-c.Done()
449 },
450 limit: 8,
451 gccgoLimit: 25,
452 },
453 } {
454 limit := test.limit
455 if runtime.Compiler == "gccgo" {
456
457
458 limit = test.gccgoLimit
459 }
460 numRuns := 100
461 if testingShort() {
462 numRuns = 10
463 }
464 if n := testingAllocsPerRun(numRuns, test.f); n > limit {
465 t.Errorf("%s allocs = %f want %d", test.desc, n, int(limit))
466 }
467 }
468 }
469
470 func XTestSimultaneousCancels(t testingT) {
471 root, cancel := WithCancel(Background())
472 m := map[Context]CancelFunc{root: cancel}
473 q := []Context{root}
474
475 for len(q) != 0 && len(m) < 100 {
476 parent := q[0]
477 q = q[1:]
478 for i := 0; i < 4; i++ {
479 ctx, cancel := WithCancel(parent)
480 m[ctx] = cancel
481 q = append(q, ctx)
482 }
483 }
484
485 var wg sync.WaitGroup
486 wg.Add(len(m))
487 for _, cancel := range m {
488 go func(cancel CancelFunc) {
489 cancel()
490 wg.Done()
491 }(cancel)
492 }
493
494 d := quiescent(t)
495 stuck := make(chan struct{})
496 timer := time.AfterFunc(d, func() { close(stuck) })
497 defer timer.Stop()
498
499
500 for ctx := range m {
501 select {
502 case <-ctx.Done():
503 case <-stuck:
504 buf := make([]byte, 10<<10)
505 n := runtime.Stack(buf, true)
506 t.Fatalf("timed out after %v waiting for <-ctx.Done(); stacks:\n%s", d, buf[:n])
507 }
508 }
509
510 done := make(chan struct{})
511 go func() {
512 wg.Wait()
513 close(done)
514 }()
515 select {
516 case <-done:
517 case <-stuck:
518 buf := make([]byte, 10<<10)
519 n := runtime.Stack(buf, true)
520 t.Fatalf("timed out after %v waiting for cancel functions; stacks:\n%s", d, buf[:n])
521 }
522 }
523
524 func XTestInterlockedCancels(t testingT) {
525 parent, cancelParent := WithCancel(Background())
526 child, cancelChild := WithCancel(parent)
527 go func() {
528 <-parent.Done()
529 cancelChild()
530 }()
531 cancelParent()
532 d := quiescent(t)
533 timer := time.NewTimer(d)
534 defer timer.Stop()
535 select {
536 case <-child.Done():
537 case <-timer.C:
538 buf := make([]byte, 10<<10)
539 n := runtime.Stack(buf, true)
540 t.Fatalf("timed out after %v waiting for child.Done(); stacks:\n%s", d, buf[:n])
541 }
542 }
543
544 func XTestLayersCancel(t testingT) {
545 testLayers(t, time.Now().UnixNano(), false)
546 }
547
548 func XTestLayersTimeout(t testingT) {
549 testLayers(t, time.Now().UnixNano(), true)
550 }
551
552 func testLayers(t testingT, seed int64, testTimeout bool) {
553 t.Parallel()
554
555 r := rand.New(rand.NewSource(seed))
556 errorf := func(format string, a ...any) {
557 t.Errorf(fmt.Sprintf("seed=%d: %s", seed, format), a...)
558 }
559 const (
560 minLayers = 30
561 )
562 type value int
563 var (
564 vals []*value
565 cancels []CancelFunc
566 numTimers int
567 ctx = Background()
568 )
569 for i := 0; i < minLayers || numTimers == 0 || len(cancels) == 0 || len(vals) == 0; i++ {
570 switch r.Intn(3) {
571 case 0:
572 v := new(value)
573 ctx = WithValue(ctx, v, v)
574 vals = append(vals, v)
575 case 1:
576 var cancel CancelFunc
577 ctx, cancel = WithCancel(ctx)
578 cancels = append(cancels, cancel)
579 case 2:
580 var cancel CancelFunc
581 d := veryLongDuration
582 if testTimeout {
583 d = shortDuration
584 }
585 ctx, cancel = WithTimeout(ctx, d)
586 cancels = append(cancels, cancel)
587 numTimers++
588 }
589 }
590 checkValues := func(when string) {
591 for _, key := range vals {
592 if val := ctx.Value(key).(*value); key != val {
593 errorf("%s: ctx.Value(%p) = %p want %p", when, key, val, key)
594 }
595 }
596 }
597 if !testTimeout {
598 select {
599 case <-ctx.Done():
600 errorf("ctx should not be canceled yet")
601 default:
602 }
603 }
604 if s, prefix := fmt.Sprint(ctx), "context.Background."; !strings.HasPrefix(s, prefix) {
605 t.Errorf("ctx.String() = %q want prefix %q", s, prefix)
606 }
607 t.Log(ctx)
608 checkValues("before cancel")
609 if testTimeout {
610 d := quiescent(t)
611 timer := time.NewTimer(d)
612 defer timer.Stop()
613 select {
614 case <-ctx.Done():
615 case <-timer.C:
616 errorf("ctx should have timed out after %v", d)
617 }
618 checkValues("after timeout")
619 } else {
620 cancel := cancels[r.Intn(len(cancels))]
621 cancel()
622 select {
623 case <-ctx.Done():
624 default:
625 errorf("ctx should be canceled")
626 }
627 checkValues("after cancel")
628 }
629 }
630
631 func XTestCancelRemoves(t testingT) {
632 checkChildren := func(when string, ctx Context, want int) {
633 if got := len(ctx.(*cancelCtx).children); got != want {
634 t.Errorf("%s: context has %d children, want %d", when, got, want)
635 }
636 }
637
638 ctx, _ := WithCancel(Background())
639 checkChildren("after creation", ctx, 0)
640 _, cancel := WithCancel(ctx)
641 checkChildren("with WithCancel child ", ctx, 1)
642 cancel()
643 checkChildren("after canceling WithCancel child", ctx, 0)
644
645 ctx, _ = WithCancel(Background())
646 checkChildren("after creation", ctx, 0)
647 _, cancel = WithTimeout(ctx, 60*time.Minute)
648 checkChildren("with WithTimeout child ", ctx, 1)
649 cancel()
650 checkChildren("after canceling WithTimeout child", ctx, 0)
651 }
652
653 func XTestWithCancelCanceledParent(t testingT) {
654 parent, pcancel := WithCancel(Background())
655 pcancel()
656
657 c, _ := WithCancel(parent)
658 select {
659 case <-c.Done():
660 default:
661 t.Errorf("child not done immediately upon construction")
662 }
663 if got, want := c.Err(), Canceled; got != want {
664 t.Errorf("child not canceled; got = %v, want = %v", got, want)
665 }
666 }
667
668 func XTestWithValueChecksKey(t testingT) {
669 panicVal := recoveredValue(func() { WithValue(Background(), []byte("foo"), "bar") })
670 if panicVal == nil {
671 t.Error("expected panic")
672 }
673 panicVal = recoveredValue(func() { WithValue(Background(), nil, "bar") })
674 if got, want := fmt.Sprint(panicVal), "nil key"; got != want {
675 t.Errorf("panic = %q; want %q", got, want)
676 }
677 }
678
679 func XTestInvalidDerivedFail(t testingT) {
680 panicVal := recoveredValue(func() { WithCancel(nil) })
681 if panicVal == nil {
682 t.Error("expected panic")
683 }
684 panicVal = recoveredValue(func() { WithDeadline(nil, time.Now().Add(shortDuration)) })
685 if panicVal == nil {
686 t.Error("expected panic")
687 }
688 panicVal = recoveredValue(func() { WithValue(nil, "foo", "bar") })
689 if panicVal == nil {
690 t.Error("expected panic")
691 }
692 }
693
694 func recoveredValue(fn func()) (v any) {
695 defer func() { v = recover() }()
696 fn()
697 return
698 }
699
700 func XTestDeadlineExceededSupportsTimeout(t testingT) {
701 i, ok := DeadlineExceeded.(interface {
702 Timeout() bool
703 })
704 if !ok {
705 t.Fatal("DeadlineExceeded does not support Timeout interface")
706 }
707 if !i.Timeout() {
708 t.Fatal("wrong value for timeout")
709 }
710 }
711
712 type myCtx struct {
713 Context
714 }
715
716 type myDoneCtx struct {
717 Context
718 }
719
720 func (d *myDoneCtx) Done() <-chan struct{} {
721 c := make(chan struct{})
722 return c
723 }
724
725 func XTestCustomContextGoroutines(t testingT) {
726 g := atomic.LoadInt32(&goroutines)
727 checkNoGoroutine := func() {
728 t.Helper()
729 now := atomic.LoadInt32(&goroutines)
730 if now != g {
731 t.Fatalf("%d goroutines created", now-g)
732 }
733 }
734 checkCreatedGoroutine := func() {
735 t.Helper()
736 now := atomic.LoadInt32(&goroutines)
737 if now != g+1 {
738 t.Fatalf("%d goroutines created, want 1", now-g)
739 }
740 g = now
741 }
742
743 _, cancel0 := WithCancel(&myDoneCtx{Background()})
744 cancel0()
745 checkCreatedGoroutine()
746
747 _, cancel0 = WithTimeout(&myDoneCtx{Background()}, veryLongDuration)
748 cancel0()
749 checkCreatedGoroutine()
750
751 checkNoGoroutine()
752 defer checkNoGoroutine()
753
754 ctx1, cancel1 := WithCancel(Background())
755 defer cancel1()
756 checkNoGoroutine()
757
758 ctx2 := &myCtx{ctx1}
759 ctx3, cancel3 := WithCancel(ctx2)
760 defer cancel3()
761 checkNoGoroutine()
762
763 _, cancel3b := WithCancel(&myDoneCtx{ctx2})
764 defer cancel3b()
765 checkCreatedGoroutine()
766
767 ctx4, cancel4 := WithTimeout(ctx3, veryLongDuration)
768 defer cancel4()
769 checkNoGoroutine()
770
771 ctx5, cancel5 := WithCancel(ctx4)
772 defer cancel5()
773 checkNoGoroutine()
774
775 cancel5()
776 checkNoGoroutine()
777
778 _, cancel6 := WithTimeout(ctx5, veryLongDuration)
779 defer cancel6()
780 checkNoGoroutine()
781
782
783 cancel6()
784 cancel1()
785 _, cancel7 := WithCancel(ctx5)
786 defer cancel7()
787 checkNoGoroutine()
788 }
789
View as plain text