Fix a deadlock in TaskLoop

This commit is contained in:
Joe Turki
2025-11-26 20:11:09 +02:00
parent 30c406b788
commit 4537cf7b6c
2 changed files with 62 additions and 0 deletions

View File

@@ -85,6 +85,8 @@ func (l *Loop) Run(ctx context.Context, t func(context.Context)) error {
select {
case <-ctx.Done():
return ctx.Err()
case <-l.done:
return ErrClosed
case l.tasks <- task{t, done}:
<-done

View File

@@ -0,0 +1,60 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package taskloop
import (
"context"
"sync/atomic"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func TestRunReturnsErrClosedWhenLoopClosing(t *testing.T) {
loop := New(func() {})
blockStarted := make(chan struct{})
releaseBlock := make(chan struct{})
go func() {
_ = loop.Run(context.Background(), func(context.Context) {
close(blockStarted)
<-releaseBlock
})
}()
<-blockStarted
var secondRan atomic.Bool
errCh := make(chan error, 1)
go func() {
errCh <- loop.Run(context.Background(), func(context.Context) {
secondRan.Store(true)
})
}()
time.Sleep(10 * time.Millisecond)
closeDone := make(chan struct{})
go func() {
loop.Close()
close(closeDone)
}()
select {
case err := <-errCh:
assert.ErrorIs(t, err, ErrClosed)
case <-time.After(time.Second):
assert.Fail(t, "Run did not return after loop close")
}
close(releaseBlock)
select {
case <-closeDone:
case <-time.After(time.Second):
assert.Fail(t, "Close did not return")
}
assert.False(t, secondRan.Load(), "second task should not excute after loop is closed")
}