aboutsummaryrefslogtreecommitdiff
path: root/src/context
diff options
context:
space:
mode:
authorJosh Bleecher Snyder <josharian@gmail.com>2017-01-08 13:22:24 -0800
committerJosh Bleecher Snyder <josharian@gmail.com>2017-02-01 20:08:41 +0000
commit986768de7fcf4def65cecd7eb0c34e2cdf92e78c (patch)
treec3b36e104e323a14a1688321486d43cdf369b5d1 /src/context
parentfd118b69fabda2f5def2f46606a0ec23f093cbb9 (diff)
downloadgo-986768de7fcf4def65cecd7eb0c34e2cdf92e78c.tar.gz
go-986768de7fcf4def65cecd7eb0c34e2cdf92e78c.zip
context: lazily initialize cancelCtx done channel
This CL reduces allocations when a context created with WithCancel either (1) never has its Done channel used or (2) gets cancelled before its Done channel is used This is not uncommon. Many contexts are created for tasks that end up not using them. name old time/op new time/op delta ContextCancelTree/depth=1/Root=Background-8 112ns ± 2% 74ns ± 1% -34.03% (p=0.000 n=17+18) ContextCancelTree/depth=1/Root=OpenCanceler-8 601ns ± 3% 544ns ± 1% -9.56% (p=0.000 n=20+20) ContextCancelTree/depth=1/Root=ClosedCanceler-8 367ns ± 4% 257ns ± 1% -30.01% (p=0.000 n=20+20) ContextCancelTree/depth=10/Root=Background-8 2.91µs ± 2% 2.87µs ± 0% -1.38% (p=0.000 n=20+18) ContextCancelTree/depth=10/Root=OpenCanceler-8 4.36µs ± 2% 4.26µs ± 1% -2.34% (p=0.000 n=20+18) ContextCancelTree/depth=10/Root=ClosedCanceler-8 2.02µs ± 2% 1.51µs ± 1% -25.18% (p=0.000 n=19+19) ContextCancelTree/depth=100/Root=Background-8 30.5µs ± 6% 30.5µs ± 1% ~ (p=0.941 n=20+20) ContextCancelTree/depth=100/Root=OpenCanceler-8 39.8µs ± 1% 41.1µs ± 1% +3.15% (p=0.000 n=18+19) ContextCancelTree/depth=100/Root=ClosedCanceler-8 17.8µs ± 1% 13.9µs ± 1% -21.61% (p=0.000 n=18+20) ContextCancelTree/depth=1000/Root=Background-8 302µs ± 1% 313µs ± 0% +3.62% (p=0.000 n=20+18) ContextCancelTree/depth=1000/Root=OpenCanceler-8 412µs ± 2% 427µs ± 1% +3.55% (p=0.000 n=18+19) ContextCancelTree/depth=1000/Root=ClosedCanceler-8 178µs ± 1% 139µs ± 1% -21.80% (p=0.000 n=19+17) name old alloc/op new alloc/op delta ContextCancelTree/depth=1/Root=Background-8 176B ± 0% 80B ± 0% -54.55% (p=0.000 n=20+20) ContextCancelTree/depth=1/Root=OpenCanceler-8 544B ± 0% 448B ± 0% -17.65% (p=0.000 n=20+20) ContextCancelTree/depth=1/Root=ClosedCanceler-8 352B ± 0% 160B ± 0% -54.55% (p=0.000 n=20+20) ContextCancelTree/depth=10/Root=Background-8 3.49kB ± 0% 3.39kB ± 0% -2.75% (p=0.000 n=20+20) ContextCancelTree/depth=10/Root=OpenCanceler-8 3.86kB ± 0% 3.76kB ± 0% -2.49% (p=0.000 n=20+20) ContextCancelTree/depth=10/Root=ClosedCanceler-8 1.94kB ± 0% 0.88kB ± 0% -54.55% (p=0.000 n=20+20) ContextCancelTree/depth=100/Root=Background-8 36.6kB ± 0% 36.5kB ± 0% -0.26% (p=0.000 n=20+20) ContextCancelTree/depth=100/Root=OpenCanceler-8 37.0kB ± 0% 36.9kB ± 0% -0.26% (p=0.000 n=20+20) ContextCancelTree/depth=100/Root=ClosedCanceler-8 17.8kB ± 0% 8.1kB ± 0% -54.55% (p=0.000 n=20+20) ContextCancelTree/depth=1000/Root=Background-8 368kB ± 0% 368kB ± 0% -0.03% (p=0.000 n=20+20) ContextCancelTree/depth=1000/Root=OpenCanceler-8 368kB ± 0% 368kB ± 0% -0.03% (p=0.000 n=20+20) ContextCancelTree/depth=1000/Root=ClosedCanceler-8 176kB ± 0% 80kB ± 0% -54.55% (p=0.000 n=20+20) name old allocs/op new allocs/op delta ContextCancelTree/depth=1/Root=Background-8 3.00 ± 0% 2.00 ± 0% -33.33% (p=0.000 n=20+20) ContextCancelTree/depth=1/Root=OpenCanceler-8 8.00 ± 0% 7.00 ± 0% -12.50% (p=0.000 n=20+20) ContextCancelTree/depth=1/Root=ClosedCanceler-8 6.00 ± 0% 4.00 ± 0% -33.33% (p=0.000 n=20+20) ContextCancelTree/depth=10/Root=Background-8 48.0 ± 0% 47.0 ± 0% -2.08% (p=0.000 n=20+20) ContextCancelTree/depth=10/Root=OpenCanceler-8 53.0 ± 0% 52.0 ± 0% -1.89% (p=0.000 n=20+20) ContextCancelTree/depth=10/Root=ClosedCanceler-8 33.0 ± 0% 22.0 ± 0% -33.33% (p=0.000 n=20+20) ContextCancelTree/depth=100/Root=Background-8 498 ± 0% 497 ± 0% -0.20% (p=0.000 n=20+20) ContextCancelTree/depth=100/Root=OpenCanceler-8 503 ± 0% 502 ± 0% -0.20% (p=0.000 n=20+20) ContextCancelTree/depth=100/Root=ClosedCanceler-8 303 ± 0% 202 ± 0% -33.33% (p=0.000 n=20+20) ContextCancelTree/depth=1000/Root=Background-8 5.00k ± 0% 5.00k ± 0% -0.02% (p=0.000 n=20+20) ContextCancelTree/depth=1000/Root=OpenCanceler-8 5.00k ± 0% 5.00k ± 0% -0.02% (p=0.000 n=20+20) ContextCancelTree/depth=1000/Root=ClosedCanceler-8 3.00k ± 0% 2.00k ± 0% -33.33% (p=0.000 n=20+20) Change-Id: Ibd7a0c3d5c847861cf1497f8fead34329413d26d Reviewed-on: https://go-review.googlesource.com/34979 Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org> Reviewed-by: Sameer Ajmani <sameer@golang.org>
Diffstat (limited to 'src/context')
-rw-r--r--src/context/context.go31
1 files changed, 22 insertions, 9 deletions
diff --git a/src/context/context.go b/src/context/context.go
index 0aa7c24df9..c60d378818 100644
--- a/src/context/context.go
+++ b/src/context/context.go
@@ -234,10 +234,7 @@ func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
// newCancelCtx returns an initialized cancelCtx.
func newCancelCtx(parent Context) cancelCtx {
- return cancelCtx{
- Context: parent,
- done: make(chan struct{}),
- }
+ return cancelCtx{Context: parent}
}
// propagateCancel arranges for child to be canceled when parent is.
@@ -306,20 +303,32 @@ type canceler interface {
Done() <-chan struct{}
}
+// closedchan is a reusable closed channel.
+var closedchan = make(chan struct{})
+
+func init() {
+ close(closedchan)
+}
+
// A cancelCtx can be canceled. When canceled, it also cancels any children
// that implement canceler.
type cancelCtx struct {
Context
- done chan struct{} // closed by the first cancel call.
-
- mu sync.Mutex
+ mu sync.Mutex // protects following fields
+ done 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
}
func (c *cancelCtx) Done() <-chan struct{} {
- return c.done
+ c.mu.Lock()
+ if c.done == nil {
+ c.done = make(chan struct{})
+ }
+ d := c.done
+ c.mu.Unlock()
+ return d
}
func (c *cancelCtx) Err() error {
@@ -344,7 +353,11 @@ func (c *cancelCtx) cancel(removeFromParent bool, err error) {
return // already canceled
}
c.err = err
- close(c.done)
+ if c.done == nil {
+ c.done = closedchan
+ } else {
+ close(c.done)
+ }
for child := range c.children {
// NOTE: acquiring the child's lock while holding parent's lock.
child.cancel(false, err)