Skip to content

Commit 64b4822

Browse files
committed
fix(internal/async): fix deadlock by removing goroutine
The function may block longer than the timeout, because it waits until get() returns. Also use synctest in order to make tests faster.
1 parent 10f6201 commit 64b4822

2 files changed

Lines changed: 36 additions & 46 deletions

File tree

internal/async/wait.go

Lines changed: 14 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ type IntervalStrategy func() <-chan time.Time
1414

1515
// WaitSyncConfig defines the waiting options.
1616
type WaitSyncConfig struct {
17-
// This method will be called from another goroutine.
1817
Get func() (value any, isTerminal bool, err error)
1918
IntervalStrategy IntervalStrategy
2019
Timeout time.Duration
@@ -48,42 +47,22 @@ func WaitSync(config *WaitSyncConfig) (terminalValue any, err error) {
4847
config.Timeout = defaultTimeout
4948
}
5049

51-
resultValue := make(chan any)
52-
resultErr := make(chan error)
53-
timeout := make(chan bool)
50+
timeoutCh := time.After(config.Timeout)
5451

55-
go func() {
56-
for {
57-
// get the payload
58-
value, stopCondition, err := config.Get()
59-
// send the payload
60-
if err != nil {
61-
resultErr <- err
62-
return
63-
}
64-
if stopCondition {
65-
resultValue <- value
66-
return
67-
}
68-
69-
// waiting for an interval before next get() call or a timeout
70-
select {
71-
case <-timeout:
72-
return
73-
case <-config.IntervalStrategy():
74-
// sleep
75-
}
52+
for {
53+
// get the payload
54+
value, stopCondition, err := config.Get()
55+
if err != nil {
56+
return nil, err
57+
}
58+
if stopCondition {
59+
return value, nil
7660
}
77-
}()
7861

79-
// waiting for a result or a timeout
80-
select {
81-
case val := <-resultValue:
82-
return val, nil
83-
case err := <-resultErr:
84-
return nil, err
85-
case <-time.After(config.Timeout):
86-
timeout <- true
87-
return nil, fmt.Errorf("timeout after %v", config.Timeout)
62+
select {
63+
case <-timeoutCh:
64+
return nil, fmt.Errorf("timeout after %v", config.Timeout)
65+
case <-config.IntervalStrategy(): // Sleep before next get() call.
66+
}
8867
}
8968
}

internal/async/wait_test.go

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,12 @@ package async
33
import (
44
"errors"
55
"testing"
6+
"testing/synctest"
67
"time"
78

89
"github.com/scaleway/scaleway-sdk-go/internal/testhelpers"
910
)
1011

11-
const flakiness = 500 * time.Millisecond
12-
1312
type value struct {
1413
doneIterations int
1514
totalDuration time.Duration
@@ -106,24 +105,36 @@ func TestWaitSync(t *testing.T) {
106105
expValue: nil,
107106
expErr: errors.New("timeout after 2s"),
108107
},
108+
{
109+
name: "WithError",
110+
config: &WaitSyncConfig{
111+
Get: func() (any, bool, error) {
112+
return nil, false, errors.New("error")
113+
},
114+
},
115+
expValue: nil,
116+
expErr: errors.New("error"),
117+
},
109118
}
110119
for _, c := range testsCases {
111120
c := c // do not remove me
112121
t.Run(c.name, func(t *testing.T) {
113122
t.Parallel()
114123

115-
terminalValue, err := WaitSync(c.config)
124+
synctest.Test(t, func(t *testing.T) {
125+
terminalValue, err := WaitSync(c.config)
116126

117-
testhelpers.Equals(t, c.expErr, err)
127+
testhelpers.Equals(t, c.expErr, err)
118128

119-
if c.expValue != nil {
120-
exp := c.expValue.(*value)
121-
acc := terminalValue.(*value)
122-
testhelpers.Equals(t, exp.doneIterations, acc.doneIterations)
129+
if c.expValue != nil {
130+
exp := c.expValue.(*value)
131+
acc := terminalValue.(*value)
132+
testhelpers.Equals(t, exp.doneIterations, acc.doneIterations)
123133

124-
ok := exp.totalDuration > acc.totalDuration-flakiness && exp.totalDuration < acc.totalDuration+flakiness
125-
testhelpers.Assert(t, ok, "totalDuration don't match the target: (acc: %v, exp: %v)", acc.totalDuration, exp.totalDuration)
126-
}
134+
ok := exp.totalDuration == acc.totalDuration
135+
testhelpers.Assert(t, ok, "totalDuration doesn't match the target: (acc: %v, exp: %v)", acc.totalDuration, exp.totalDuration)
136+
}
137+
})
127138
})
128139
}
129140
}

0 commit comments

Comments
 (0)