]> Cypherpunks.ru repositories - gostls13.git/commitdiff
context: add WithoutCancel
authorSameer Ajmani <sameer@google.com>
Tue, 28 Mar 2023 21:44:18 +0000 (17:44 -0400)
committerSameer Ajmani <sameer@golang.org>
Wed, 29 Mar 2023 20:41:09 +0000 (20:41 +0000)
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>

api/next/40221.txt [new file with mode: 0644]
src/context/context.go
src/context/context_test.go
src/context/x_test.go

diff --git a/api/next/40221.txt b/api/next/40221.txt
new file mode 100644 (file)
index 0000000..ed51340
--- /dev/null
@@ -0,0 +1 @@
+pkg context, func WithoutCancel(Context) Context #40221
index c60179e70fb0a0d6ea1df9b3a61436a961713e99..7227099889a83bcddd89077482cca955de194b1d 100644 (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
index 5311d8d4f4fead3ddd40d4dd923cb8dae23fb547..e14b040d1a1d4ec60c5324e37be908231279197c 100644 (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)
+       }
+}
index a2d814f8ead768bac71376473e9b350fb0226587..00f546bbf70fdf8d094eebc13b5e497035bfc22a 100644 (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) }