1// Copyright 2014 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5package context 6 7import ( 8 "fmt" 9 "math/rand" 10 "runtime" 11 "strings" 12 "sync" 13 "sync/atomic" 14 "time" 15) 16 17type testingT interface { 18 Deadline() (time.Time, bool) 19 Error(args ...interface{}) 20 Errorf(format string, args ...interface{}) 21 Fail() 22 FailNow() 23 Failed() bool 24 Fatal(args ...interface{}) 25 Fatalf(format string, args ...interface{}) 26 Helper() 27 Log(args ...interface{}) 28 Logf(format string, args ...interface{}) 29 Name() string 30 Parallel() 31 Skip(args ...interface{}) 32 SkipNow() 33 Skipf(format string, args ...interface{}) 34 Skipped() bool 35} 36 37// otherContext is a Context that's not one of the types defined in context.go. 38// This lets us test code paths that differ based on the underlying type of the 39// Context. 40type otherContext struct { 41 Context 42} 43 44const ( 45 shortDuration = 1 * time.Millisecond // a reasonable duration to block in a test 46 veryLongDuration = 1000 * time.Hour // an arbitrary upper bound on the test's running time 47) 48 49// quiescent returns an arbitrary duration by which the program should have 50// completed any remaining work and reached a steady (idle) state. 51func 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 61func 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 76func 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 91func 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() // Should propagate synchronously. 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 130func contains(m map[canceler]struct{}, key canceler) bool { 131 _, ret := m[key] 132 return ret 133} 134 135func XTestParentFinishesChild(t testingT) { 136 // Context tree: 137 // parent -> cancelChild 138 // parent -> valueChild -> timerChild 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 // The parent's children should contain the two cancelable children. 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 // parent and children should all be finished. 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 // WithCancel should return a canceled context on a canceled parent. 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 212func 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) // pcok == false when parent == Background() 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 // child should be finished. 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 // parent should not be finished. 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 272func 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 287func 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 312func 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 331func XTestCanceledTimeout(t testingT) { 332 c, _ := WithTimeout(Background(), time.Second) 333 o := otherContext{c} 334 c, cancel := WithTimeout(o, veryLongDuration) 335 cancel() // Should propagate synchronously. 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 346type key1 int 347type key2 int 348 349var k1 = key1(1) 350var k2 = key2(1) // same int as k1, different type 351var k3 = key2(3) // same type as k2, different int 352 353func 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 401func 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 // gccgo does not yet do escape analysis. 457 // TODO(iant): Remove this when gccgo does do escape analysis. 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 470func XTestSimultaneousCancels(t testingT) { 471 root, cancel := WithCancel(Background()) 472 m := map[Context]CancelFunc{root: cancel} 473 q := []Context{root} 474 // Create a tree of contexts. 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 // Start all the cancels in a random order. 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 // Wait on all the contexts in a random order. 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 // Wait for all the cancel functions to return. 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 524func 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 544func XTestLayersCancel(t testingT) { 545 testLayers(t, time.Now().UnixNano(), false) 546} 547 548func XTestLayersTimeout(t testingT) { 549 testLayers(t, time.Now().UnixNano(), true) 550} 551 552func testLayers(t testingT, seed int64, testTimeout bool) { 553 t.Parallel() 554 555 r := rand.New(rand.NewSource(seed)) 556 errorf := func(format string, a ...interface{}) { 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 631func 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 653func 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 cancelled; got = %v, want = %v", got, want) 665 } 666} 667 668func 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 679func 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 694func recoveredValue(fn func()) (v interface{}) { 695 defer func() { v = recover() }() 696 fn() 697 return 698} 699 700func 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 712type myCtx struct { 713 Context 714} 715 716type myDoneCtx struct { 717 Context 718} 719 720func (d *myDoneCtx) Done() <-chan struct{} { 721 c := make(chan struct{}) 722 return c 723} 724 725func 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() // ctx1 is not providing Done, must not be used 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 // Check applied to cancelled context. 783 cancel6() 784 cancel1() 785 _, cancel7 := WithCancel(ctx5) 786 defer cancel7() 787 checkNoGoroutine() 788} 789