@@ -428,7 +428,7 @@ type cancelCtx struct {
428
428
mu sync.Mutex // protects following fields
429
429
done atomic.Value // of chan struct{}, created lazily, closed by first cancel call
430
430
children map [canceler ]struct {} // set to nil by the first cancel call
431
- err error // set to non-nil by the first cancel call
431
+ err atomic. Value // set to non-nil by the first cancel call
432
432
cause error // set to non-nil by the first cancel call
433
433
}
434
434
@@ -455,10 +455,11 @@ func (c *cancelCtx) Done() <-chan struct{} {
455
455
}
456
456
457
457
func (c * cancelCtx ) Err () error {
458
- c .mu .Lock ()
459
- err := c .err
460
- c .mu .Unlock ()
461
- return err
458
+ // An atomic load is ~5x faster than a mutex, which can matter in tight loops.
459
+ if err := c .err .Load (); err != nil {
460
+ return err .(error )
461
+ }
462
+ return nil
462
463
}
463
464
464
465
// propagateCancel arranges for child to be canceled when parent is.
@@ -482,9 +483,9 @@ func (c *cancelCtx) propagateCancel(parent Context, child canceler) {
482
483
if p , ok := parentCancelCtx (parent ); ok {
483
484
// parent is a *cancelCtx, or derives from one.
484
485
p .mu .Lock ()
485
- if p . err != nil {
486
+ if err := p . err . Load (); err != nil {
486
487
// parent has already been canceled
487
- child .cancel (false , p . err , p .cause )
488
+ child .cancel (false , err .( error ) , p .cause )
488
489
} else {
489
490
if p .children == nil {
490
491
p .children = make (map [canceler ]struct {})
@@ -545,11 +546,11 @@ func (c *cancelCtx) cancel(removeFromParent bool, err, cause error) {
545
546
cause = err
546
547
}
547
548
c .mu .Lock ()
548
- if c .err != nil {
549
+ if c .err . Load () != nil {
549
550
c .mu .Unlock ()
550
551
return // already canceled
551
552
}
552
- c .err = err
553
+ c .err . Store ( err )
553
554
c .cause = cause
554
555
d , _ := c .done .Load ().(chan struct {})
555
556
if d == nil {
@@ -639,7 +640,7 @@ func WithDeadlineCause(parent Context, d time.Time, cause error) (Context, Cance
639
640
}
640
641
c .mu .Lock ()
641
642
defer c .mu .Unlock ()
642
- if c .err == nil {
643
+ if c .err . Load () == nil {
643
644
c .timer = time .AfterFunc (dur , func () {
644
645
c .cancel (true , DeadlineExceeded , cause )
645
646
})
0 commit comments