mirror of
https://github.com/golang/go
synced 2024-11-11 22:30:21 -07:00
context: reduce contention in cancelCtx.Done
Use an atomic.Value to hold the done channel. Conveniently, we have a mutex handy to coordinate writes to it. name old time/op new time/op delta ContextCancelDone-8 67.5ns ±10% 2.2ns ±11% -96.74% (p=0.000 n=30+28) Fixes #42564 Change-Id: I5d72e0e87fb221d4e230209e5fb4698bea4053c6 Reviewed-on: https://go-review.googlesource.com/c/go/+/288193 Trust: Josh Bleecher Snyder <josharian@gmail.com> Trust: Sameer Ajmani <sameer@golang.org> Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Bryan C. Mills <bcmills@google.com>
This commit is contained in:
parent
691ac806d2
commit
ae1fa08e41
@ -5,6 +5,7 @@
|
||||
package context_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
. "context"
|
||||
"fmt"
|
||||
"runtime"
|
||||
@ -138,3 +139,17 @@ func BenchmarkCheckCanceled(b *testing.B) {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkContextCancelDone(b *testing.B) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
default:
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -303,10 +303,8 @@ func parentCancelCtx(parent Context) (*cancelCtx, bool) {
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
p.mu.Lock()
|
||||
ok = p.done == done
|
||||
p.mu.Unlock()
|
||||
if !ok {
|
||||
pdone, _ := p.done.Load().(chan struct{})
|
||||
if pdone != done {
|
||||
return nil, false
|
||||
}
|
||||
return p, true
|
||||
@ -345,7 +343,7 @@ type cancelCtx struct {
|
||||
Context
|
||||
|
||||
mu sync.Mutex // protects following fields
|
||||
done chan struct{} // created lazily, closed by first cancel call
|
||||
done atomic.Value // of chan struct{}, created lazily, closed by first cancel call
|
||||
children map[canceler]struct{} // set to nil by the first cancel call
|
||||
err error // set to non-nil by the first cancel call
|
||||
}
|
||||
@ -358,13 +356,18 @@ func (c *cancelCtx) Value(key interface{}) interface{} {
|
||||
}
|
||||
|
||||
func (c *cancelCtx) Done() <-chan struct{} {
|
||||
c.mu.Lock()
|
||||
if c.done == nil {
|
||||
c.done = make(chan struct{})
|
||||
d := c.done.Load()
|
||||
if d != nil {
|
||||
return d.(chan struct{})
|
||||
}
|
||||
d := c.done
|
||||
c.mu.Unlock()
|
||||
return d
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
d = c.done.Load()
|
||||
if d == nil {
|
||||
d = make(chan struct{})
|
||||
c.done.Store(d)
|
||||
}
|
||||
return d.(chan struct{})
|
||||
}
|
||||
|
||||
func (c *cancelCtx) Err() error {
|
||||
@ -401,10 +404,11 @@ func (c *cancelCtx) cancel(removeFromParent bool, err error) {
|
||||
return // already canceled
|
||||
}
|
||||
c.err = err
|
||||
if c.done == nil {
|
||||
c.done = closedchan
|
||||
d, _ := c.done.Load().(chan struct{})
|
||||
if d == nil {
|
||||
c.done.Store(closedchan)
|
||||
} else {
|
||||
close(c.done)
|
||||
close(d)
|
||||
}
|
||||
for child := range c.children {
|
||||
// NOTE: acquiring the child's lock while holding parent's lock.
|
||||
|
Loading…
Reference in New Issue
Block a user