Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit4fc0093

Browse files
authored
fix: fix TestCloserStack_Timeout to wait for all asyncClosers (#19837)
fixescoder/internal#966TestCloserStack_Timeout creates `asyncCloser`s which allow control over the exact timing and order of their close method returning. They also, as a final backstop will throw an error if the test context ends before they are unblocked.TestCloserStack_Timeout unblocks all `asyncCloser`s in a defer and then ends the test. This defer _unblocks_ the running close goroutines, but does not wait for them to finish. Since the test context is canceled as soon as the test completes, this creates a race condition where the close goroutines can trigger the context cancelled arm of the `select` statement.The fix is to both unblock and wait for all close goroutines to complete before ending the test and cancelling the context.
1 parente6b04d1 commit4fc0093

File tree

1 file changed

+21
-16
lines changed

1 file changed

+21
-16
lines changed

‎cli/ssh_internal_test.go‎

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ func TestCloserStack_CloseAfterContext(t *testing.T) {
158158
logger:=slogtest.Make(t,&slogtest.Options{IgnoreErrors:true}).Leveled(slog.LevelDebug)
159159
uut:=newCloserStack(ctx,logger,quartz.NewMock(t))
160160
ac:=newAsyncCloser(testCtx,t)
161-
deferac.complete()
161+
deferac.unblock()
162162
err:=uut.push("async",ac)
163163
require.NoError(t,err)
164164
cancel()
@@ -178,8 +178,9 @@ func TestCloserStack_CloseAfterContext(t *testing.T) {
178178
t.Fatal("closed before stack was finished")
179179
}
180180

181-
ac.complete()
181+
ac.unblock()
182182
testutil.TryReceive(testCtx,t,closed)
183+
testutil.TryReceive(testCtx,t,ac.done)
183184
}
184185

185186
funcTestCloserStack_Timeout(t*testing.T) {
@@ -198,7 +199,8 @@ func TestCloserStack_Timeout(t *testing.T) {
198199
}
199200
deferfunc() {
200201
for_,a:=rangeac {
201-
a.complete()
202+
a.unblock()
203+
testutil.TryReceive(ctx,t,a.done)// ensure we don't race with context cancellation
202204
}
203205
}()
204206

@@ -215,7 +217,7 @@ func TestCloserStack_Timeout(t *testing.T) {
215217
testutil.TryReceive(ctx,t,ac[1].started)
216218

217219
// middle one finishes
218-
ac[1].complete()
220+
ac[1].unblock()
219221
// bottom starts, but also hangs
220222
testutil.TryReceive(ctx,t,ac[0].started)
221223

@@ -317,34 +319,37 @@ func (c *fakeCloser) Close() error {
317319
}
318320

319321
typeasyncCloserstruct {
320-
t*testing.T
321-
ctx context.Context
322-
startedchanstruct{}
323-
isCompletechanstruct{}
324-
comepleteOnce sync.Once
322+
t*testing.T
323+
ctx context.Context
324+
startedchanstruct{}
325+
donechanstruct{}
326+
isUnblockedchanstruct{}
327+
unblockOnce sync.Once
325328
}
326329

327330
func (c*asyncCloser)Close()error {
328331
close(c.started)
332+
deferclose(c.done)
329333
select {
330334
case<-c.ctx.Done():
331335
c.t.Error("timed out")
332336
returnc.ctx.Err()
333-
case<-c.isComplete:
337+
case<-c.isUnblocked:
334338
returnnil
335339
}
336340
}
337341

338-
func (c*asyncCloser)complete() {
339-
c.comepleteOnce.Do(func() {close(c.isComplete) })
342+
func (c*asyncCloser)unblock() {
343+
c.unblockOnce.Do(func() {close(c.isUnblocked) })
340344
}
341345

342346
funcnewAsyncCloser(ctx context.Context,t*testing.T)*asyncCloser {
343347
return&asyncCloser{
344-
t:t,
345-
ctx:ctx,
346-
isComplete:make(chanstruct{}),
347-
started:make(chanstruct{}),
348+
t:t,
349+
ctx:ctx,
350+
isUnblocked:make(chanstruct{}),
351+
started:make(chanstruct{}),
352+
done:make(chanstruct{}),
348353
}
349354
}
350355

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp