Skip to content

Commit ad772a4

Browse files
RaduBerinderickystewart
authored andcommitted
cockroach: context: add PropagateCancel
1 parent 28622c1 commit ad772a4

File tree

2 files changed

+50
-11
lines changed

2 files changed

+50
-11
lines changed

api/go1.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30869,3 +30869,4 @@ pkg unicode/utf8, func RuneLen(int32) int
3086930869
pkg unicode/utf8, func RuneStart(uint8) bool
3087030870
pkg unicode/utf8, func Valid([]uint8) bool
3087130871
pkg unicode/utf8, func ValidString(string) bool
30872+
pkg context, func PropagateCancel(Context, func()) bool

src/context/context.go

Lines changed: 49 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -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 {
371372
var goroutines atomic.Int32
372373

373374
// &cancelCtxKey is the key that a cancelCtx returns itself for.
375+
//go:linkname cancelCtxKey
374376
var 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+
531539
type 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

Comments
 (0)