1
0
mirror of https://github.com/golang/go synced 2024-09-23 15:20:13 -06:00

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 <sameer@golang.org>
Reviewed-by: Ian Lance Taylor <iant@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
This commit is contained in:
Sameer Ajmani 2023-03-28 17:44:18 -04:00 committed by Sameer Ajmani
parent c292397160
commit 1844b54166
4 changed files with 91 additions and 0 deletions

1
api/next/40221.txt Normal file
View File

@ -0,0 +1 @@
pkg context, func WithoutCancel(Context) Context #40221

View File

@ -474,6 +474,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
@ -645,6 +679,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

View File

@ -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)
@ -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)
}
}

View File

@ -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) }