@@ -232,97 +232,97 @@ type CancelFunc func()
232
232
// call cancel as soon as the operations running in this Context complete.
233
233
func WithCancel (parent Context ) (ctx Context , cancel CancelFunc ) {
234
234
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 ) }
237
237
}
238
238
239
239
// newCancelCtx returns an initialized cancelCtx.
240
240
func newCancelCtx (parent Context ) cancelCtx {
241
241
return cancelCtx {Context : parent }
242
242
}
243
243
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 )
270
254
}
271
255
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 {
276
274
for {
277
275
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 ()
284
280
default :
285
- return nil , false
281
+ return & cancelerCtx { Context : parent }
286
282
}
287
283
}
288
284
}
289
285
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
-
310
286
// closedchan is a reusable closed channel.
311
287
var closedchan = make (chan struct {})
312
288
313
289
func init () {
314
290
close (closedchan )
315
291
}
316
292
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
+
317
316
// A cancelCtx can be canceled. When canceled, it also cancels any children
318
317
// that implement canceler.
319
318
type cancelCtx struct {
320
319
Context
321
320
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
326
326
}
327
327
328
328
func (c * cancelCtx ) Done () <- chan struct {} {
@@ -342,6 +342,29 @@ func (c *cancelCtx) Err() error {
342
342
return err
343
343
}
344
344
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
+
345
368
type stringer interface {
346
369
String () string
347
370
}
@@ -357,9 +380,9 @@ func (c *cancelCtx) String() string {
357
380
return contextName (c .Context ) + ".WithCancel"
358
381
}
359
382
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
361
384
// 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 ) {
363
386
if err == nil {
364
387
panic ("context: internal error: missing cancel error" )
365
388
}
@@ -376,14 +399,17 @@ func (c *cancelCtx) cancel(removeFromParent bool, err error) {
376
399
}
377
400
for child := range c .children {
378
401
// NOTE: acquiring the child's lock while holding parent's lock.
379
- child .cancel ( false , err )
402
+ child .Cancel ( err )
380
403
}
381
404
c .children = nil
382
405
c .mu .Unlock ()
406
+ }
383
407
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 ()
387
413
}
388
414
389
415
// 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) {
404
430
cancelCtx : newCancelCtx (parent ),
405
431
deadline : d ,
406
432
}
407
- propagateCancel (parent , c )
433
+ c . link = newLink (parent , c )
408
434
dur := time .Until (d )
409
435
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 ) }
412
438
}
413
439
c .mu .Lock ()
414
440
defer c .mu .Unlock ()
415
441
if c .err == nil {
416
442
c .timer = time .AfterFunc (dur , func () {
417
- c .cancel ( true , DeadlineExceeded )
443
+ c .cancelAndUnlink ( DeadlineExceeded )
418
444
})
419
445
}
420
- return c , func () { c .cancel ( true , Canceled ) }
446
+ return c , func () { c .cancelAndUnlink ( Canceled ) }
421
447
}
422
448
423
449
// A timerCtx carries a timer and a deadline. It embeds a cancelCtx to
@@ -440,12 +466,8 @@ func (c *timerCtx) String() string {
440
466
time .Until (c .deadline ).String () + "])"
441
467
}
442
468
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 )
449
471
c .mu .Lock ()
450
472
if c .timer != nil {
451
473
c .timer .Stop ()
@@ -523,3 +545,27 @@ func (c *valueCtx) Value(key interface{}) interface{} {
523
545
}
524
546
return c .Context .Value (key )
525
547
}
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