From 2a20f901dbd7922a27b5625a8a442587c8c3df2c Mon Sep 17 00:00:00 2001 From: Aleksandr Nogikh Date: Thu, 17 Apr 2025 19:45:47 +0200 Subject: 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. --- pkg/manager/repro.go | 6 +++++- pkg/manager/repro_test.go | 22 ++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) (limited to 'pkg') 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 -- cgit mrf-deployment