mirror of
https://github.com/golang/go
synced 2024-11-08 10:36:10 -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
|
package context_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
. "context"
|
. "context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"runtime"
|
"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 {
|
if !ok {
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
p.mu.Lock()
|
pdone, _ := p.done.Load().(chan struct{})
|
||||||
ok = p.done == done
|
if pdone != done {
|
||||||
p.mu.Unlock()
|
|
||||||
if !ok {
|
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
return p, true
|
return p, true
|
||||||
@ -345,7 +343,7 @@ type cancelCtx struct {
|
|||||||
Context
|
Context
|
||||||
|
|
||||||
mu sync.Mutex // protects following fields
|
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
|
children map[canceler]struct{} // set to nil by the first cancel call
|
||||||
err error // set to non-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{} {
|
func (c *cancelCtx) Done() <-chan struct{} {
|
||||||
c.mu.Lock()
|
d := c.done.Load()
|
||||||
if c.done == nil {
|
if d != nil {
|
||||||
c.done = make(chan struct{})
|
return d.(chan struct{})
|
||||||
}
|
}
|
||||||
d := c.done
|
c.mu.Lock()
|
||||||
c.mu.Unlock()
|
defer c.mu.Unlock()
|
||||||
return d
|
d = c.done.Load()
|
||||||
|
if d == nil {
|
||||||
|
d = make(chan struct{})
|
||||||
|
c.done.Store(d)
|
||||||
|
}
|
||||||
|
return d.(chan struct{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *cancelCtx) Err() error {
|
func (c *cancelCtx) Err() error {
|
||||||
@ -401,10 +404,11 @@ func (c *cancelCtx) cancel(removeFromParent bool, err error) {
|
|||||||
return // already canceled
|
return // already canceled
|
||||||
}
|
}
|
||||||
c.err = err
|
c.err = err
|
||||||
if c.done == nil {
|
d, _ := c.done.Load().(chan struct{})
|
||||||
c.done = closedchan
|
if d == nil {
|
||||||
|
c.done.Store(closedchan)
|
||||||
} else {
|
} else {
|
||||||
close(c.done)
|
close(d)
|
||||||
}
|
}
|
||||||
for child := range c.children {
|
for child := range c.children {
|
||||||
// NOTE: acquiring the child's lock while holding parent's lock.
|
// NOTE: acquiring the child's lock while holding parent's lock.
|
||||||
|
Loading…
Reference in New Issue
Block a user