Skip to content

Commit

Permalink
context: add WithoutCancel
Browse files Browse the repository at this point in the history
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 golang#40221

Change-Id: Ide29631c08881176a2c2a58409fed9ca6072e65d
Reviewed-on: https://go-review.googlesource.com/c/go/+/479918
Run-TryBot: Sameer Ajmani <sameer@golang.org>
Reviewed-by: Ian Lance Taylor <iant@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
  • Loading branch information
Sajmani authored and eric committed Aug 23, 2023
1 parent facd667 commit a1e38ad
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 0 deletions.
1 change: 1 addition & 0 deletions api/next/40221.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pkg context, func WithoutCancel(Context) Context #40221
41 changes: 41 additions & 0 deletions src/context/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,40 @@ func (c *cancelCtx) cancel(removeFromParent bool, err, cause error) {
}
}

// 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.
func WithoutCancel(parent Context) Context {
if parent == nil {
panic("cannot create context from nil parent")
}
return withoutCancelCtx{parent}
}

type withoutCancelCtx struct {
c Context
}

func (withoutCancelCtx) Deadline() (deadline time.Time, ok bool) {
return
}

func (withoutCancelCtx) Done() <-chan struct{} {
return nil
}

func (withoutCancelCtx) Err() error {
return nil
}

func (c withoutCancelCtx) Value(key any) any {
return value(c, key)
}

func (c withoutCancelCtx) String() string {
return contextName(c.c) + ".WithoutCancel"
}

// WithDeadline returns a copy of the parent context with the deadline adjusted
// to be no later than d. If the parent's deadline is already earlier than d,
// WithDeadline(parent, d) is semantically equivalent to parent. The returned
Expand Down Expand Up @@ -653,6 +687,13 @@ func value(c Context, key any) any {
return c
}
c = ctx.Context
case withoutCancelCtx:
if key == &cancelCtxKey {
// This implements Cause(ctx) == nil
// when ctx is created using WithoutCancel.
return nil
}
c = ctx.c
case *timerCtx:
if key == &cancelCtxKey {
return ctx.cancelCtx
Expand Down
48 changes: 48 additions & 0 deletions src/context/context_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -981,6 +981,36 @@ func XTestCause(t testingT) {
err: Canceled,
cause: finishedEarly,
},
{
name: "WithoutCancel",
ctx: func() Context {
return WithoutCancel(Background())
}(),
err: nil,
cause: nil,
},
{
name: "WithoutCancel canceled",
ctx: func() Context {
ctx, cancel := WithCancelCause(Background())
ctx = WithoutCancel(ctx)
cancel(finishedEarly)
return ctx
}(),
err: nil,
cause: nil,
},
{
name: "WithoutCancel timeout",
ctx: func() Context {
ctx, cancel := WithTimeoutCause(Background(), 0, tooSlow)
ctx = WithoutCancel(ctx)
cancel()
return ctx
}(),
err: nil,
cause: nil,
},
} {
if got, want := test.ctx.Err(), test.err; want != got {
t.Errorf("%s: ctx.Err() = %v want %v", test.name, got, want)
Expand Down Expand Up @@ -1009,3 +1039,21 @@ func XTestCauseRace(t testingT) {
runtime.Gosched()
}
}

func XTestWithoutCancel(t testingT) {
key, value := "key", "value"
ctx := WithValue(Background(), key, value)
ctx = WithoutCancel(ctx)
if d, ok := ctx.Deadline(); !d.IsZero() || ok != false {
t.Errorf("ctx.Deadline() = %v, %v want zero, false", d, ok)
}
if done := ctx.Done(); done != nil {
t.Errorf("ctx.Deadline() = %v want nil", done)
}
if err := ctx.Err(); err != nil {
t.Errorf("ctx.Err() = %v want nil", err)
}
if v := ctx.Value(key); v != value {
t.Errorf("ctx.Value(%q) = %q want %q", key, v, value)
}
}
1 change: 1 addition & 0 deletions src/context/x_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,4 @@ func TestDeadlineExceededSupportsTimeout(t *testing.T) { XTestDeadlineExceededSu
func TestCustomContextGoroutines(t *testing.T) { XTestCustomContextGoroutines(t) }
func TestCause(t *testing.T) { XTestCause(t) }
func TestCauseRace(t *testing.T) { XTestCauseRace(t) }
func TestWithoutCancel(t *testing.T) { XTestWithoutCancel(t) }

0 comments on commit a1e38ad

Please sign in to comment.