Skip to content

Commit 0a0e6af

Browse files
neildgopherbot
authored andcommitted
context: use atomic operation in ctx.Err
oos: darwin goarch: arm64 pkg: context cpu: Apple M1 Pro │ /tmp/bench.0.mac │ /tmp/bench.1.mac │ │ sec/op │ sec/op vs base │ ErrOK-10 13.750n ± 1% 2.080n ± 0% -84.87% (p=0.000 n=10) ErrCanceled-10 13.530n ± 1% 3.248n ± 1% -76.00% (p=0.000 n=10) geomean 13.64n 2.599n -80.94% goos: linux goarch: amd64 pkg: context cpu: Intel(R) Xeon(R) CPU @ 2.30GHz │ /tmp/bench.0.linux │ /tmp/bench.1.linux │ │ sec/op │ sec/op vs base │ ErrOK-16 21.435n ± 0% 4.243n ± 0% -80.21% (p=0.000 n=10) ErrCanceled-16 21.445n ± 0% 5.070n ± 0% -76.36% (p=0.000 n=10) geomean 21.44n 4.638n -78.37% Fixes #72040 Change-Id: I3b337ab1934689d2da4134492ee7c5aac8f92845 Reviewed-on: https://go-review.googlesource.com/c/go/+/653795 LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]> Auto-Submit: Damien Neil <[email protected]> Reviewed-by: Alan Donovan <[email protected]>
1 parent 14647b0 commit 0a0e6af

File tree

2 files changed

+31
-10
lines changed

2 files changed

+31
-10
lines changed

src/context/benchmark_test.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,3 +188,23 @@ func BenchmarkDeepValueSameGoRoutine(b *testing.B) {
188188
})
189189
}
190190
}
191+
192+
func BenchmarkErrOK(b *testing.B) {
193+
ctx, cancel := WithCancel(Background())
194+
defer cancel()
195+
for b.Loop() {
196+
if err := ctx.Err(); err != nil {
197+
b.Fatalf("ctx.Err() = %v", err)
198+
}
199+
}
200+
}
201+
202+
func BenchmarkErrCanceled(b *testing.B) {
203+
ctx, cancel := WithCancel(Background())
204+
cancel()
205+
for b.Loop() {
206+
if err := ctx.Err(); err == nil {
207+
b.Fatalf("ctx.Err() = %v", err)
208+
}
209+
}
210+
}

src/context/context.go

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -428,7 +428,7 @@ type cancelCtx struct {
428428
mu sync.Mutex // protects following fields
429429
done atomic.Value // of chan struct{}, created lazily, closed by first cancel call
430430
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
432432
cause error // set to non-nil by the first cancel call
433433
}
434434

@@ -455,10 +455,11 @@ func (c *cancelCtx) Done() <-chan struct{} {
455455
}
456456

457457
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
462463
}
463464

464465
// propagateCancel arranges for child to be canceled when parent is.
@@ -482,9 +483,9 @@ func (c *cancelCtx) propagateCancel(parent Context, child canceler) {
482483
if p, ok := parentCancelCtx(parent); ok {
483484
// parent is a *cancelCtx, or derives from one.
484485
p.mu.Lock()
485-
if p.err != nil {
486+
if err := p.err.Load(); err != nil {
486487
// parent has already been canceled
487-
child.cancel(false, p.err, p.cause)
488+
child.cancel(false, err.(error), p.cause)
488489
} else {
489490
if p.children == nil {
490491
p.children = make(map[canceler]struct{})
@@ -545,11 +546,11 @@ func (c *cancelCtx) cancel(removeFromParent bool, err, cause error) {
545546
cause = err
546547
}
547548
c.mu.Lock()
548-
if c.err != nil {
549+
if c.err.Load() != nil {
549550
c.mu.Unlock()
550551
return // already canceled
551552
}
552-
c.err = err
553+
c.err.Store(err)
553554
c.cause = cause
554555
d, _ := c.done.Load().(chan struct{})
555556
if d == nil {
@@ -639,7 +640,7 @@ func WithDeadlineCause(parent Context, d time.Time, cause error) (Context, Cance
639640
}
640641
c.mu.Lock()
641642
defer c.mu.Unlock()
642-
if c.err == nil {
643+
if c.err.Load() == nil {
643644
c.timer = time.AfterFunc(dur, func() {
644645
c.cancel(true, DeadlineExceeded, cause)
645646
})

0 commit comments

Comments
 (0)