--- /dev/null
+pkg context, func WithDeadlineCause(Context, time.Time, error) (Context, CancelFunc) #56661
+pkg context, func WithTimeoutCause(Context, time.Duration, error) (Context, CancelFunc) #56661
// Canceling this context releases resources associated with it, so code should
// call cancel as soon as the operations running in this Context complete.
func WithDeadline(parent Context, d time.Time) (Context, CancelFunc) {
+ return WithDeadlineCause(parent, d, nil)
+}
+
+// WithDeadlineCause behaves like WithDeadline but also sets the cause of the
+// returned Context when the deadline is exceeded. The returned CancelFunc does
+// not set the cause.
+func WithDeadlineCause(parent Context, d time.Time, cause error) (Context, CancelFunc) {
if parent == nil {
panic("cannot create context from nil parent")
}
propagateCancel(parent, c)
dur := time.Until(d)
if dur <= 0 {
- c.cancel(true, DeadlineExceeded, nil) // deadline has already passed
+ c.cancel(true, DeadlineExceeded, cause) // deadline has already passed
return c, func() { c.cancel(false, Canceled, nil) }
}
c.mu.Lock()
defer c.mu.Unlock()
if c.err == nil {
c.timer = time.AfterFunc(dur, func() {
- c.cancel(true, DeadlineExceeded, nil)
+ c.cancel(true, DeadlineExceeded, cause)
})
}
return c, func() { c.cancel(true, Canceled, nil) }
return WithDeadline(parent, time.Now().Add(timeout))
}
+// WithTimeoutCause behaves like WithTimeout but also sets the cause of the
+// returned Context when the timout expires. The returned CancelFunc does
+// not set the cause.
+func WithTimeoutCause(parent Context, timeout time.Duration, cause error) (Context, CancelFunc) {
+ return WithDeadlineCause(parent, time.Now().Add(timeout), cause)
+}
+
// WithValue returns a copy of parent in which the value associated with key is
// val.
//
func XTestCause(t testingT) {
var (
- parentCause = fmt.Errorf("parentCause")
- childCause = fmt.Errorf("childCause")
+ forever = 1e6 * time.Second
+ parentCause = fmt.Errorf("parentCause")
+ childCause = fmt.Errorf("childCause")
+ tooSlow = fmt.Errorf("tooSlow")
+ finishedEarly = fmt.Errorf("finishedEarly")
)
for _, test := range []struct {
name string
err: DeadlineExceeded,
cause: DeadlineExceeded,
},
+ {
+ name: "WithTimeout canceled",
+ ctx: func() Context {
+ ctx, cancel := WithTimeout(Background(), forever)
+ cancel()
+ return ctx
+ }(),
+ err: Canceled,
+ cause: Canceled,
+ },
+ {
+ name: "WithTimeoutCause",
+ ctx: func() Context {
+ ctx, cancel := WithTimeoutCause(Background(), 0, tooSlow)
+ cancel()
+ return ctx
+ }(),
+ err: DeadlineExceeded,
+ cause: tooSlow,
+ },
+ {
+ name: "WithTimeoutCause canceled",
+ ctx: func() Context {
+ ctx, cancel := WithTimeoutCause(Background(), forever, tooSlow)
+ cancel()
+ return ctx
+ }(),
+ err: Canceled,
+ cause: Canceled,
+ },
+ {
+ name: "WithTimeoutCause stacked",
+ ctx: func() Context {
+ ctx, cancel := WithCancelCause(Background())
+ ctx, _ = WithTimeoutCause(ctx, 0, tooSlow)
+ cancel(finishedEarly)
+ return ctx
+ }(),
+ err: DeadlineExceeded,
+ cause: tooSlow,
+ },
+ {
+ name: "WithTimeoutCause stacked canceled",
+ ctx: func() Context {
+ ctx, cancel := WithCancelCause(Background())
+ ctx, _ = WithTimeoutCause(ctx, forever, tooSlow)
+ cancel(finishedEarly)
+ return ctx
+ }(),
+ err: Canceled,
+ cause: finishedEarly,
+ },
} {
if got, want := test.ctx.Err(), test.err; want != got {
t.Errorf("%s: ctx.Err() = %v want %v", test.name, got, want)