Skip to content

Commit a5bd71c

Browse files
committed
context2: add WithRetimeout
The most common reason you want to use WithoutCancellation is to call something after the primary context has been already cancelled. However, in most cases you should have a new timeout so things don't get stuck. Change-Id: Id9e33363cc67e96c582c802ada5ca422b8d66ee8
1 parent 0608f30 commit a5bd71c

File tree

2 files changed

+22
-0
lines changed

2 files changed

+22
-0
lines changed

context2/nocancel.go

+5
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,8 @@ func (ctx noCancelContext) String() string {
4747
func (ctx noCancelContext) Value(key interface{}) interface{} {
4848
return ctx.ctx.Value(key)
4949
}
50+
51+
// WithRetimeout allows to create a new timeout for a potentially cancelled context.
52+
func WithRetimeout(ctx context.Context, duration time.Duration) (context.Context, context.CancelFunc) {
53+
return context.WithTimeout(WithoutCancellation(ctx), duration)
54+
}

context2/nocancel_test.go

+17
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package context2_test
66
import (
77
"context"
88
"testing"
9+
"time"
910

1011
"github.com/stretchr/testify/require"
1112

@@ -24,3 +25,19 @@ func TestWithoutCancellation(t *testing.T) {
2425
require.Equal(t, error(nil), without.Err())
2526
require.Equal(t, (<-chan struct{})(nil), without.Done())
2627
}
28+
29+
func TestWithRetimeout(t *testing.T) {
30+
t.Parallel()
31+
ctx := testcontext.New(t)
32+
33+
parent, cancel := context.WithCancel(ctx)
34+
cancel()
35+
36+
start := time.Now()
37+
subctx, subcancel := context2.WithRetimeout(parent, 3*time.Second)
38+
<-subctx.Done()
39+
finish := time.Now()
40+
subcancel()
41+
42+
require.Greater(t, finish.Sub(start), time.Second)
43+
}

0 commit comments

Comments
 (0)