Skip to content

Commit 751da59

Browse files
kerthcetsywhangabhinav
authored
Make maxSleep and maxRetires configurable when building options (#94)
Make goleak options more flexible to adapt to users' variety scenarios Signed-off-by: Kante Yin <[email protected]> fix #93 --------- Signed-off-by: Kante Yin <[email protected]> Co-authored-by: Sung Yoon Whang <[email protected]> Co-authored-by: Abhinav Gupta <[email protected]>
1 parent 21e4cb1 commit 751da59

File tree

4 files changed

+64
-11
lines changed

4 files changed

+64
-11
lines changed

leaks.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ func Find(options ...Option) error {
5656
cur := stack.Current().ID()
5757

5858
opts := buildOpts(options...)
59+
if err := opts.validate(); err != nil {
60+
return err
61+
}
5962
if opts.cleanup != nil {
6063
return errors.New("Cleanup can only be passed to VerifyNone or VerifyTestMain")
6164
}

leaks_test.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ var _ = TestingT(testing.TB(nil))
3636
// testOptions passes a shorter max sleep time, used so tests don't wait
3737
// ~1 second in cases where we expect Find to error out.
3838
func testOptions() Option {
39-
return maxSleep(time.Millisecond)
39+
return MaxSleepInterval(time.Millisecond)
4040
}
4141

4242
func TestFind(t *testing.T) {
@@ -60,6 +60,16 @@ func TestFind(t *testing.T) {
6060
err := Find(Cleanup(func(int) { assert.Fail(t, "this should not be called") }))
6161
require.Error(t, err, "Should exit with invalid option")
6262
})
63+
64+
t.Run("Find should return error when maxRetries is less than 0", func(t *testing.T) {
65+
err := Find(MaxRetryAttempts(-1))
66+
require.Error(t, err, "maxRetries should be greater than 0")
67+
})
68+
69+
t.Run("Find should return error when maxSleep is less than 0s", func(t *testing.T) {
70+
err := Find(MaxSleepInterval(time.Duration(-1)))
71+
require.Error(t, err, "maxSleep should be greater than 0s")
72+
})
6373
}
6474

6575
func TestFindRetry(t *testing.T) {

options.go

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
package goleak
2222

2323
import (
24+
"errors"
2425
"strings"
2526
"time"
2627

@@ -32,10 +33,14 @@ type Option interface {
3233
apply(*opts)
3334
}
3435

35-
// We retry up to 20 times if we can't find the goroutine that
36-
// we are looking for. In between each attempt, we will sleep for
37-
// a short while to let any running goroutines complete.
38-
const _defaultRetries = 20
36+
const (
37+
// We retry up to default 20 times if we can't find the goroutine that
38+
// we are looking for.
39+
_defaultRetryAttempts = 20
40+
// In between each retry attempt, sleep for up to default 100 microseconds
41+
// to let any running goroutine completes.
42+
_defaultSleepInterval = 100 * time.Microsecond
43+
)
3944

4045
type opts struct {
4146
filters []func(stack.Stack) bool
@@ -53,6 +58,17 @@ func (o *opts) apply(opts *opts) {
5358
opts.cleanup = o.cleanup
5459
}
5560

61+
// validate the options.
62+
func (o *opts) validate() error {
63+
if o.maxRetries < 0 {
64+
return errors.New("maxRetryAttempts should be greater than 0")
65+
}
66+
if o.maxSleep <= 0 {
67+
return errors.New("maxSleepInterval should be greater than 0s")
68+
}
69+
return nil
70+
}
71+
5672
// optionFunc lets us easily write options without a custom type.
5773
type optionFunc func(*opts)
5874

@@ -91,12 +107,25 @@ func IgnoreCurrent() Option {
91107
})
92108
}
93109

94-
func maxSleep(d time.Duration) Option {
110+
// MaxSleepInterval sets the maximum sleep time in-between each retry attempt.
111+
// The sleep duration grows in an exponential backoff, to a maximum of the value specified here.
112+
// If not configured, default to 100 microseconds.
113+
func MaxSleepInterval(d time.Duration) Option {
95114
return optionFunc(func(opts *opts) {
96115
opts.maxSleep = d
97116
})
98117
}
99118

119+
// MaxRetryAttempts sets the retry upper limit.
120+
// When finding extra goroutines, we'll retry until all goroutines complete
121+
// or end up with the maximum retry attempts.
122+
// If not configured, default to 20 times.
123+
func MaxRetryAttempts(num int) Option {
124+
return optionFunc(func(opts *opts) {
125+
opts.maxRetries = num
126+
})
127+
}
128+
100129
func addFilter(f func(stack.Stack) bool) Option {
101130
return optionFunc(func(opts *opts) {
102131
opts.filters = append(opts.filters, f)
@@ -105,8 +134,8 @@ func addFilter(f func(stack.Stack) bool) Option {
105134

106135
func buildOpts(options ...Option) *opts {
107136
opts := &opts{
108-
maxRetries: _defaultRetries,
109-
maxSleep: 100 * time.Millisecond,
137+
maxRetries: _defaultRetryAttempts,
138+
maxSleep: _defaultSleepInterval,
110139
}
111140
opts.filters = append(opts.filters,
112141
isTestStack,
@@ -117,6 +146,7 @@ func buildOpts(options ...Option) *opts {
117146
for _, option := range options {
118147
option.apply(opts)
119148
}
149+
120150
return opts
121151
}
122152

options_test.go

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,20 @@ func TestOptionsFilters(t *testing.T) {
6565
require.Zero(t, countUnfiltered(), "blockedG should be filtered out. running: %v", stack.All())
6666
}
6767

68-
func TestOptionsRetry(t *testing.T) {
68+
func TestBuildOptions(t *testing.T) {
69+
// With default options.
6970
opts := buildOpts()
70-
opts.maxRetries = 50 // initial attempt + 50 retries = 11
71-
opts.maxSleep = time.Millisecond
71+
assert.Equal(t, _defaultSleepInterval, opts.maxSleep, "value of maxSleep not right")
72+
assert.Equal(t, _defaultRetryAttempts, opts.maxRetries, "value of maxRetries not right")
73+
74+
// With customized options.
75+
opts = buildOpts(MaxRetryAttempts(50), MaxSleepInterval(time.Microsecond))
76+
assert.Equal(t, time.Microsecond, opts.maxSleep, "value of maxSleep not right")
77+
assert.Equal(t, 50, opts.maxRetries, "value of maxRetries not right")
78+
}
79+
80+
func TestOptionsRetry(t *testing.T) {
81+
opts := buildOpts(MaxSleepInterval(time.Millisecond), MaxRetryAttempts(50)) // initial attempt + 50 retries = 51
7282

7383
for i := 0; i < 50; i++ {
7484
assert.True(t, opts.retry(i), "Attempt %v/51 should allow retrying", i)

0 commit comments

Comments
 (0)