Skip to content

Commit 1844b54

Browse files
committed
context: add WithoutCancel
WithoutCancel returns a copy of parent that is not canceled when parent is canceled. The returned context returns no Deadline or Err, and its Done channel is nil. Calling Cause on the returned context returns nil. API changes: +pkg context, func WithoutCancel(Context) Context Fixes #40221 Change-Id: Ide29631c08881176a2c2a58409fed9ca6072e65d Reviewed-on: https://go-review.googlesource.com/c/go/+/479918 Run-TryBot: Sameer Ajmani <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]> TryBot-Result: Gopher Robot <[email protected]>
1 parent c292397 commit 1844b54

File tree

4 files changed

+91
-0
lines changed

4 files changed

+91
-0
lines changed

api/next/40221.txt

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pkg context, func WithoutCancel(Context) Context #40221

src/context/context.go

+41
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,40 @@ func (c *cancelCtx) cancel(removeFromParent bool, err, cause error) {
474474
}
475475
}
476476

477+
// WithoutCancel returns a copy of parent that is not canceled when parent is canceled.
478+
// The returned context returns no Deadline or Err, and its Done channel is nil.
479+
// Calling Cause on the returned context returns nil.
480+
func WithoutCancel(parent Context) Context {
481+
if parent == nil {
482+
panic("cannot create context from nil parent")
483+
}
484+
return withoutCancelCtx{parent}
485+
}
486+
487+
type withoutCancelCtx struct {
488+
c Context
489+
}
490+
491+
func (withoutCancelCtx) Deadline() (deadline time.Time, ok bool) {
492+
return
493+
}
494+
495+
func (withoutCancelCtx) Done() <-chan struct{} {
496+
return nil
497+
}
498+
499+
func (withoutCancelCtx) Err() error {
500+
return nil
501+
}
502+
503+
func (c withoutCancelCtx) Value(key any) any {
504+
return value(c, key)
505+
}
506+
507+
func (c withoutCancelCtx) String() string {
508+
return contextName(c.c) + ".WithoutCancel"
509+
}
510+
477511
// WithDeadline returns a copy of the parent context with the deadline adjusted
478512
// to be no later than d. If the parent's deadline is already earlier than d,
479513
// WithDeadline(parent, d) is semantically equivalent to parent. The returned
@@ -645,6 +679,13 @@ func value(c Context, key any) any {
645679
return c
646680
}
647681
c = ctx.Context
682+
case withoutCancelCtx:
683+
if key == &cancelCtxKey {
684+
// This implements Cause(ctx) == nil
685+
// when ctx is created using WithoutCancel.
686+
return nil
687+
}
688+
c = ctx.c
648689
case *timerCtx:
649690
if key == &cancelCtxKey {
650691
return &ctx.cancelCtx

src/context/context_test.go

+48
Original file line numberDiff line numberDiff line change
@@ -981,6 +981,36 @@ func XTestCause(t testingT) {
981981
err: Canceled,
982982
cause: finishedEarly,
983983
},
984+
{
985+
name: "WithoutCancel",
986+
ctx: func() Context {
987+
return WithoutCancel(Background())
988+
}(),
989+
err: nil,
990+
cause: nil,
991+
},
992+
{
993+
name: "WithoutCancel canceled",
994+
ctx: func() Context {
995+
ctx, cancel := WithCancelCause(Background())
996+
ctx = WithoutCancel(ctx)
997+
cancel(finishedEarly)
998+
return ctx
999+
}(),
1000+
err: nil,
1001+
cause: nil,
1002+
},
1003+
{
1004+
name: "WithoutCancel timeout",
1005+
ctx: func() Context {
1006+
ctx, cancel := WithTimeoutCause(Background(), 0, tooSlow)
1007+
ctx = WithoutCancel(ctx)
1008+
cancel()
1009+
return ctx
1010+
}(),
1011+
err: nil,
1012+
cause: nil,
1013+
},
9841014
} {
9851015
if got, want := test.ctx.Err(), test.err; want != got {
9861016
t.Errorf("%s: ctx.Err() = %v want %v", test.name, got, want)
@@ -1009,3 +1039,21 @@ func XTestCauseRace(t testingT) {
10091039
runtime.Gosched()
10101040
}
10111041
}
1042+
1043+
func XTestWithoutCancel(t testingT) {
1044+
key, value := "key", "value"
1045+
ctx := WithValue(Background(), key, value)
1046+
ctx = WithoutCancel(ctx)
1047+
if d, ok := ctx.Deadline(); !d.IsZero() || ok != false {
1048+
t.Errorf("ctx.Deadline() = %v, %v want zero, false", d, ok)
1049+
}
1050+
if done := ctx.Done(); done != nil {
1051+
t.Errorf("ctx.Deadline() = %v want nil", done)
1052+
}
1053+
if err := ctx.Err(); err != nil {
1054+
t.Errorf("ctx.Err() = %v want nil", err)
1055+
}
1056+
if v := ctx.Value(key); v != value {
1057+
t.Errorf("ctx.Value(%q) = %q want %q", key, v, value)
1058+
}
1059+
}

src/context/x_test.go

+1
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,4 @@ func TestDeadlineExceededSupportsTimeout(t *testing.T) { XTestDeadlineExceededSu
3131
func TestCustomContextGoroutines(t *testing.T) { XTestCustomContextGoroutines(t) }
3232
func TestCause(t *testing.T) { XTestCause(t) }
3333
func TestCauseRace(t *testing.T) { XTestCauseRace(t) }
34+
func TestWithoutCancel(t *testing.T) { XTestWithoutCancel(t) }

0 commit comments

Comments
 (0)