*: update context pkg

This commit is contained in:
Xiang Li 2015-03-30 18:43:31 -07:00
parent 77a04cda0c
commit 73adb20166
3 changed files with 64 additions and 27 deletions

2
Godeps/Godeps.json generated
View File

@ -84,7 +84,7 @@
}, },
{ {
"ImportPath": "golang.org/x/net/context", "ImportPath": "golang.org/x/net/context",
"Rev": "b8c11bbe941633d84573af9686bfef280594b4fd" "Rev": "7dbad50ab5b31073856416cdcfeb2796d682f844"
} }
] ]
} }

View File

@ -64,16 +64,19 @@ type Context interface {
// //
// Done is provided for use in select statements: // Done is provided for use in select statements:
// //
// // DoSomething calls DoSomethingSlow and returns as soon as // // Stream generates values with DoSomething and sends them to out
// // it returns or ctx.Done is closed. // // until DoSomething returns an error or ctx.Done is closed.
// func DoSomething(ctx context.Context) (Result, error) { // func Stream(ctx context.Context, out <-chan Value) error {
// c := make(chan Result, 1) // for {
// go func() { c <- DoSomethingSlow(ctx) }() // v, err := DoSomething(ctx)
// if err != nil {
// return err
// }
// select { // select {
// case res := <-c:
// return res, nil
// case <-ctx.Done(): // case <-ctx.Done():
// return nil, ctx.Err() // return ctx.Err()
// case out <- v:
// }
// } // }
// } // }
// //
@ -202,6 +205,9 @@ type CancelFunc func()
// WithCancel returns a copy of parent with a new Done channel. The returned // WithCancel returns a copy of parent with a new Done channel. The returned
// context's Done channel is closed when the returned cancel function is called // context's Done channel is closed when the returned cancel function is called
// or when the parent context's Done channel is closed, whichever happens first. // or when the parent context's Done channel is closed, whichever happens first.
//
// Canceling this context releases resources associated with it, so code should
// call cancel as soon as the operations running in this Context complete.
func WithCancel(parent Context) (ctx Context, cancel CancelFunc) { func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
c := newCancelCtx(parent) c := newCancelCtx(parent)
propagateCancel(parent, &c) propagateCancel(parent, &c)
@ -262,6 +268,19 @@ func parentCancelCtx(parent Context) (*cancelCtx, bool) {
} }
} }
// removeChild removes a context from its parent.
func removeChild(parent Context, child canceler) {
p, ok := parentCancelCtx(parent)
if !ok {
return
}
p.mu.Lock()
if p.children != nil {
delete(p.children, child)
}
p.mu.Unlock()
}
// A canceler is a context type that can be canceled directly. The // A canceler is a context type that can be canceled directly. The
// implementations are *cancelCtx and *timerCtx. // implementations are *cancelCtx and *timerCtx.
type canceler interface { type canceler interface {
@ -316,13 +335,7 @@ func (c *cancelCtx) cancel(removeFromParent bool, err error) {
c.mu.Unlock() c.mu.Unlock()
if removeFromParent { if removeFromParent {
if p, ok := parentCancelCtx(c.Context); ok { removeChild(c.Context, c)
p.mu.Lock()
if p.children != nil {
delete(p.children, c)
}
p.mu.Unlock()
}
} }
} }
@ -333,9 +346,8 @@ func (c *cancelCtx) cancel(removeFromParent bool, err error) {
// cancel function is called, or when the parent context's Done channel is // cancel function is called, or when the parent context's Done channel is
// closed, whichever happens first. // closed, whichever happens first.
// //
// Canceling this context releases resources associated with the deadline // Canceling this context releases resources associated with it, so code should
// timer, so code should call cancel as soon as the operations running in this // call cancel as soon as the operations running in this Context complete.
// Context complete.
func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) { func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) {
if cur, ok := parent.Deadline(); ok && cur.Before(deadline) { if cur, ok := parent.Deadline(); ok && cur.Before(deadline) {
// The current deadline is already sooner than the new one. // The current deadline is already sooner than the new one.
@ -380,7 +392,11 @@ func (c *timerCtx) String() string {
} }
func (c *timerCtx) cancel(removeFromParent bool, err error) { func (c *timerCtx) cancel(removeFromParent bool, err error) {
c.cancelCtx.cancel(removeFromParent, err) c.cancelCtx.cancel(false, err)
if removeFromParent {
// Remove this timerCtx from its parent cancelCtx's children.
removeChild(c.cancelCtx.Context, c)
}
c.mu.Lock() c.mu.Lock()
if c.timer != nil { if c.timer != nil {
c.timer.Stop() c.timer.Stop()
@ -391,9 +407,8 @@ func (c *timerCtx) cancel(removeFromParent bool, err error) {
// WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)). // WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)).
// //
// Canceling this context releases resources associated with the deadline // Canceling this context releases resources associated with it, so code should
// timer, so code should call cancel as soon as the operations running in this // call cancel as soon as the operations running in this Context complete:
// Context complete:
// //
// func slowOperationWithTimeout(ctx context.Context) (Result, error) { // func slowOperationWithTimeout(ctx context.Context) (Result, error) {
// ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond) // ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond)

View File

@ -551,3 +551,25 @@ func testLayers(t *testing.T, seed int64, testTimeout bool) {
checkValues("after cancel") checkValues("after cancel")
} }
} }
func TestCancelRemoves(t *testing.T) {
checkChildren := func(when string, ctx Context, want int) {
if got := len(ctx.(*cancelCtx).children); got != want {
t.Errorf("%s: context has %d children, want %d", when, got, want)
}
}
ctx, _ := WithCancel(Background())
checkChildren("after creation", ctx, 0)
_, cancel := WithCancel(ctx)
checkChildren("with WithCancel child ", ctx, 1)
cancel()
checkChildren("after cancelling WithCancel child", ctx, 0)
ctx, _ = WithCancel(Background())
checkChildren("after creation", ctx, 0)
_, cancel = WithTimeout(ctx, 60*time.Minute)
checkChildren("with WithTimeout child ", ctx, 1)
cancel()
checkChildren("after cancelling WithTimeout child", ctx, 0)
}