aboutsummaryrefslogtreecommitdiffstats
path: root/pkg
diff options
context:
space:
mode:
authorAleksandr Nogikh <nogikh@google.com>2025-04-17 19:45:47 +0200
committerDmitry Vyukov <dvyukov@google.com>2025-04-17 19:46:40 +0000
commit2a20f901dbd7922a27b5625a8a442587c8c3df2c (patch)
tree9e0cdcbf507597afd7e0e40bd017fb3de14e5d92 /pkg
parent2a6ededbf54a9f8ac036ad0ebfc673934f93fde9 (diff)
pkg/manager: prevent deadlock on ReproLoop cancellation
When the context is cancelled, no one may be polling the pingQueue, yet we write to it synchronously. Wrap it in a select{} statement and add a test to ensure that the loop reacts properly to the context cancellation.
Diffstat (limited to 'pkg')
-rw-r--r--pkg/manager/repro.go6
-rw-r--r--pkg/manager/repro_test.go22
2 files changed, 27 insertions, 1 deletions
diff --git a/pkg/manager/repro.go b/pkg/manager/repro.go
index 1531f3d8d..c9eb2f9a9 100644
--- a/pkg/manager/repro.go
+++ b/pkg/manager/repro.go
@@ -230,7 +230,11 @@ func (r *ReproLoop) Loop(ctx context.Context) {
r.mu.Unlock()
r.parallel <- struct{}{}
- r.pingQueue <- struct{}{}
+ // If the context is cancelled, no one is listening on pingQueue.
+ select {
+ case r.pingQueue <- struct{}{}:
+ default:
+ }
}()
}
}
diff --git a/pkg/manager/repro_test.go b/pkg/manager/repro_test.go
index def436e99..d66bee8d0 100644
--- a/pkg/manager/repro_test.go
+++ b/pkg/manager/repro_test.go
@@ -123,6 +123,28 @@ func TestReproRWRace(t *testing.T) {
mock.onVMShutdown(t, obj)
}
+func TestCancelRunningRepro(t *testing.T) {
+ mock := &reproMgrMock{
+ run: make(chan runCallback),
+ }
+ obj := NewReproLoop(mock, 1, false)
+ ctx, done := context.WithCancel(context.Background())
+ complete := make(chan struct{})
+ go func() {
+ obj.Loop(ctx)
+ close(complete)
+ }()
+
+ defer func() {
+ <-complete
+ }()
+
+ obj.Enqueue(&Crash{Report: &report.Report{Title: "A"}})
+ obj.Enqueue(&Crash{Report: &report.Report{Title: "B"}})
+ <-mock.run
+ done()
+}
+
type reproMgrMock struct {
reserved atomic.Int64
run chan runCallback