@@ -62,6 +62,7 @@ import (
6262 "sync"
6363 "sync/atomic"
6464 "time"
65+ _ "unsafe" // for go:linkname
6566)
6667
6768// A Context carries a deadline, a cancellation signal, and other values across
@@ -371,6 +372,7 @@ type stopCtx struct {
371372var goroutines atomic.Int32
372373
373374// &cancelCtxKey is the key that a cancelCtx returns itself for.
375+ //go:linkname cancelCtxKey
374376var cancelCtxKey int
375377
376378// parentCancelCtx returns the underlying *cancelCtx for parent.
@@ -490,17 +492,7 @@ func (c *cancelCtx) propagateCancel(parent Context, child canceler) {
490492
491493 if p , ok := parentCancelCtx (parent ); ok {
492494 // parent is a *cancelCtx, or derives from one.
493- p .mu .Lock ()
494- if err := p .err .Load (); err != nil {
495- // parent has already been canceled
496- child .cancel (false , err .(error ), p .cause )
497- } else {
498- if p .children == nil {
499- p .children = make (map [canceler ]struct {})
500- }
501- p .children [child ] = struct {}{}
502- }
503- p .mu .Unlock ()
495+ p .addChild (child )
504496 return
505497 }
506498
@@ -528,6 +520,22 @@ func (c *cancelCtx) propagateCancel(parent Context, child canceler) {
528520 }()
529521}
530522
523+ // addChild adds child to the list of children.
524+ // NB: CockroachDB runtime patch.
525+ func (c * cancelCtx ) addChild (child canceler ) {
526+ c .mu .Lock ()
527+ if err := c .err .Load (); err != nil {
528+ // parent has already been canceled
529+ child .cancel (false , err .(error ), c .cause )
530+ } else {
531+ if c .children == nil {
532+ c .children = make (map [canceler ]struct {})
533+ }
534+ c .children [child ] = struct {}{}
535+ }
536+ c .mu .Unlock ()
537+ }
538+
531539type stringer interface {
532540 String () string
533541}
@@ -804,3 +812,33 @@ func value(c Context, key any) any {
804812 }
805813 }
806814}
815+
816+ // CockroachDB runtime patch.
817+ // cancelerAdapter invokes f when cancel context completes.
818+ type cancelerAdapter struct {
819+ * cancelCtx
820+ f func ()
821+ }
822+
823+ func (c * cancelerAdapter ) cancel (removeFromParent bool , err , cause error ) {
824+ if removeFromParent {
825+ removeChild (c .cancelCtx , c )
826+ }
827+ c .f ()
828+ }
829+
830+ // PropagateCancel arranges for f to be invoked when parent is done.
831+ // Parent must be one of the cancelable contexts.
832+ // Returns true if cancellation will be propagated, false if the parent
833+ // is not cancelable.
834+ // This is similar to AfterFunc(), but does not spin up goroutine, and instead
835+ // invokes f on whatever goroutine completed parent context.
836+ func PropagateCancel (parent Context , f func ()) bool {
837+ p , ok := parent .Value (& cancelCtxKey ).(* cancelCtx )
838+ if ! ok {
839+ return false
840+ }
841+ a := cancelerAdapter {cancelCtx : p , f : f }
842+ p .addChild (& a )
843+ return true
844+ }
0 commit comments