@@ -232,97 +232,97 @@ type CancelFunc func()
232232// call cancel as soon as the operations running in this Context complete.
233233func WithCancel (parent Context ) (ctx Context , cancel CancelFunc ) {
234234 c := newCancelCtx (parent )
235- propagateCancel (parent , & c )
236- return & c , func () { c .cancel ( true , Canceled ) }
235+ c . link = newLink (parent , & c )
236+ return & c , func () { c .cancelAndUnlink ( Canceled ) }
237237}
238238
239239// newCancelCtx returns an initialized cancelCtx.
240240func newCancelCtx (parent Context ) cancelCtx {
241241 return cancelCtx {Context : parent }
242242}
243243
244- // propagateCancel arranges for child to be canceled when parent is.
245- func propagateCancel (parent Context , child canceler ) {
246- if parent .Done () == nil {
247- return // parent is never canceled
248- }
249- if p , ok := parentCancelCtx (parent ); ok {
250- p .mu .Lock ()
251- if p .err != nil {
252- // parent has already been canceled
253- child .cancel (false , p .err )
254- } else {
255- if p .children == nil {
256- p .children = make (map [canceler ]struct {})
257- }
258- p .children [child ] = struct {}{}
259- }
260- p .mu .Unlock ()
261- } else {
262- go func () {
263- select {
264- case <- parent .Done ():
265- child .cancel (false , parent .Err ())
266- case <- child .Done ():
267- }
268- }()
269- }
244+ // canceler is an optional interface for a context type for propagating
245+ // cancellation. If a context implementation want to manage context
246+ // cancellation, it needs to cancel all its children when cancelled.
247+ // This interface provides a way for a child to register itself in a
248+ // parent context.
249+ type canceler interface {
250+ // Register a new cancelable child for a context.
251+ Register (cancelable )
252+ // Unregister existing cancelable child.
253+ Unregister (cancelable )
270254}
271255
272- // parentCancelCtx follows a chain of parent references until it finds a
273- // *cancelCtx. This function understands how each of the concrete types in this
274- // package represents its parent.
275- func parentCancelCtx (parent Context ) (* cancelCtx , bool ) {
256+ // cancelable is an optional interface for a context type that can be canceled
257+ // directly.
258+ type cancelable interface {
259+ Cancel (err error )
260+ Done () <- chan struct {}
261+ }
262+
263+ // child is an optional interface for a context type in which it returns its
264+ // parent context. It is useful for context to expose this interface to prevent
265+ // an extra goroutine when being called with a cancelable context conversion.
266+ type child interface {
267+ // Return the parent context of a context.
268+ Parent () Context
269+ }
270+
271+ // lookupCanceler follows a chain of parent references until it finds a
272+ // canceler.
273+ func lookupCanceler (parent Context ) canceler {
276274 for {
277275 switch c := parent .(type ) {
278- case * cancelCtx :
279- return c , true
280- case * timerCtx :
281- return & c .cancelCtx , true
282- case * valueCtx :
283- parent = c .Context
276+ case canceler :
277+ return c
278+ case child :
279+ parent = c .Parent ()
284280 default :
285- return nil , false
281+ return & cancelerCtx { Context : parent }
286282 }
287283 }
288284}
289285
290- // removeChild removes a context from its parent.
291- func removeChild (parent Context , child canceler ) {
292- p , ok := parentCancelCtx (parent )
293- if ! ok {
294- return
295- }
296- p .mu .Lock ()
297- if p .children != nil {
298- delete (p .children , child )
299- }
300- p .mu .Unlock ()
301- }
302-
303- // A canceler is a context type that can be canceled directly. The
304- // implementations are *cancelCtx and *timerCtx.
305- type canceler interface {
306- cancel (removeFromParent bool , err error )
307- Done () <- chan struct {}
308- }
309-
310286// closedchan is a reusable closed channel.
311287var closedchan = make (chan struct {})
312288
313289func init () {
314290 close (closedchan )
315291}
316292
293+ // link maintains a link between a child cancelable to a parent canceler.
294+ type link struct {
295+ parent canceler
296+ child cancelable
297+ }
298+
299+ func newLink (parent Context , child cancelable ) link {
300+ if parent .Done () == nil {
301+ return link {} // parent is never canceled
302+ }
303+ c := lookupCanceler (parent )
304+ c .Register (child )
305+ return link {parent : c , child : child }
306+ }
307+
308+ func (l * link ) unlink () {
309+ if l .parent != nil {
310+ l .parent .Unregister (l .child )
311+ l .parent = nil
312+ l .child = nil
313+ }
314+ }
315+
317316// A cancelCtx can be canceled. When canceled, it also cancels any children
318317// that implement canceler.
319318type cancelCtx struct {
320319 Context
321320
322- mu sync.Mutex // protects following fields
323- done chan struct {} // created lazily, closed by first cancel call
324- children map [canceler ]struct {} // set to nil by the first cancel call
325- err error // set to non-nil by the first cancel call
321+ mu sync.Mutex // protects following fields
322+ done chan struct {} // created lazily, closed by first cancel call
323+ children map [cancelable ]struct {} // set to nil by the first cancel call
324+ err error // set to non-nil by the first cancel call
325+ link link
326326}
327327
328328func (c * cancelCtx ) Done () <- chan struct {} {
@@ -342,6 +342,29 @@ func (c *cancelCtx) Err() error {
342342 return err
343343}
344344
345+ func (c * cancelCtx ) Register (child cancelable ) {
346+ c .mu .Lock ()
347+ if c .err != nil {
348+ // parent has already been canceled
349+ child .Cancel (c .err )
350+ } else {
351+ if c .children == nil {
352+ c .children = make (map [cancelable ]struct {})
353+ }
354+ c .children [child ] = struct {}{}
355+ }
356+ c .mu .Unlock ()
357+ }
358+
359+ // Unregister removes a context from the context.
360+ func (c * cancelCtx ) Unregister (child cancelable ) {
361+ c .mu .Lock ()
362+ if c .children != nil {
363+ delete (c .children , child )
364+ }
365+ c .mu .Unlock ()
366+ }
367+
345368type stringer interface {
346369 String () string
347370}
@@ -357,9 +380,9 @@ func (c *cancelCtx) String() string {
357380 return contextName (c .Context ) + ".WithCancel"
358381}
359382
360- // cancel closes c.done, cancels each of c's children, and, if
383+ // Cancel closes c.done, cancels each of c's children, and, if
361384// removeFromParent is true, removes c from its parent's children.
362- func (c * cancelCtx ) cancel ( removeFromParent bool , err error ) {
385+ func (c * cancelCtx ) Cancel ( err error ) {
363386 if err == nil {
364387 panic ("context: internal error: missing cancel error" )
365388 }
@@ -376,14 +399,17 @@ func (c *cancelCtx) cancel(removeFromParent bool, err error) {
376399 }
377400 for child := range c .children {
378401 // NOTE: acquiring the child's lock while holding parent's lock.
379- child .cancel ( false , err )
402+ child .Cancel ( err )
380403 }
381404 c .children = nil
382405 c .mu .Unlock ()
406+ }
383407
384- if removeFromParent {
385- removeChild (c .Context , c )
386- }
408+ func (c * cancelCtx ) cancelAndUnlink (err error ) {
409+ c .Cancel (err )
410+ c .mu .Lock ()
411+ c .link .unlink ()
412+ c .mu .Unlock ()
387413}
388414
389415// WithDeadline returns a copy of the parent context with the deadline adjusted
@@ -404,20 +430,20 @@ func WithDeadline(parent Context, d time.Time) (Context, CancelFunc) {
404430 cancelCtx : newCancelCtx (parent ),
405431 deadline : d ,
406432 }
407- propagateCancel (parent , c )
433+ c . link = newLink (parent , c )
408434 dur := time .Until (d )
409435 if dur <= 0 {
410- c .cancel ( true , DeadlineExceeded ) // deadline has already passed
411- return c , func () { c .cancel ( false , Canceled ) }
436+ c .cancelAndUnlink ( DeadlineExceeded ) // deadline has already passed
437+ return c , func () { c .Cancel ( Canceled ) }
412438 }
413439 c .mu .Lock ()
414440 defer c .mu .Unlock ()
415441 if c .err == nil {
416442 c .timer = time .AfterFunc (dur , func () {
417- c .cancel ( true , DeadlineExceeded )
443+ c .cancelAndUnlink ( DeadlineExceeded )
418444 })
419445 }
420- return c , func () { c .cancel ( true , Canceled ) }
446+ return c , func () { c .cancelAndUnlink ( Canceled ) }
421447}
422448
423449// A timerCtx carries a timer and a deadline. It embeds a cancelCtx to
@@ -440,12 +466,8 @@ func (c *timerCtx) String() string {
440466 time .Until (c .deadline ).String () + "])"
441467}
442468
443- func (c * timerCtx ) cancel (removeFromParent bool , err error ) {
444- c .cancelCtx .cancel (false , err )
445- if removeFromParent {
446- // Remove this timerCtx from its parent cancelCtx's children.
447- removeChild (c .cancelCtx .Context , c )
448- }
469+ func (c * timerCtx ) Cancel (err error ) {
470+ c .cancelCtx .Cancel (err )
449471 c .mu .Lock ()
450472 if c .timer != nil {
451473 c .timer .Stop ()
@@ -523,3 +545,27 @@ func (c *valueCtx) Value(key interface{}) interface{} {
523545 }
524546 return c .Context .Value (key )
525547}
548+
549+ func (c * valueCtx ) Parent () Context {
550+ return c .Context
551+ }
552+
553+ // cancelerCtx wraps a context and implement the canceler interface
554+ // by invoking a goroutine for every registered cancelable.
555+ type cancelerCtx struct {
556+ Context
557+ }
558+
559+ func (r * cancelerCtx ) Register (child cancelable ) {
560+ go func () {
561+ select {
562+ case <- r .Done ():
563+ child .Cancel (r .Err ())
564+ case <- child .Done ():
565+ }
566+ }()
567+ }
568+
569+ func (r * cancelerCtx ) Unregister (child cancelable ) {
570+
571+ }
0 commit comments