From feffdbbcf20c661b4b29c3bb7461ac00d29124f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20K=C5=99epinsk=C3=BD?= Date: Fri, 5 Dec 2025 15:50:32 +0100 Subject: [PATCH] mark QuotaMonitor as not running and invalidate monitors list to prevent close of closed channel panic --- pkg/controller/garbagecollector/graph_builder.go | 5 +++-- pkg/controller/resourcequota/resource_quota_monitor.go | 10 ++++++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/pkg/controller/garbagecollector/graph_builder.go b/pkg/controller/garbagecollector/graph_builder.go index 7348576e291..9b3b7fc4844 100644 --- a/pkg/controller/garbagecollector/graph_builder.go +++ b/pkg/controller/garbagecollector/graph_builder.go @@ -94,8 +94,9 @@ type GraphBuilder struct { // This channel is also protected by monitorLock. stopCh <-chan struct{} - // running tracks whether Run() has been called. - // it is protected by monitorLock. + // running is set to true when the Run() function has been called. + // It will revert to false when the Run() function receives a cancellation. + // It is protected by monitorLock. running bool eventRecorder record.EventRecorder diff --git a/pkg/controller/resourcequota/resource_quota_monitor.go b/pkg/controller/resourcequota/resource_quota_monitor.go index ec687001df4..28aef8be539 100644 --- a/pkg/controller/resourcequota/resource_quota_monitor.go +++ b/pkg/controller/resourcequota/resource_quota_monitor.go @@ -79,8 +79,9 @@ type QuotaMonitor struct { // This channel is also protected by monitorLock. stopCh <-chan struct{} - // running tracks whether Run() has been called. - // it is protected by monitorLock. + // running is set to true when the Run() function has been called. + // It will revert to false when the Run() function receives a cancellation. + // It is protected by monitorLock. running bool // monitors are the producer of the resourceChanges queue @@ -333,6 +334,10 @@ func (qm *QuotaMonitor) Run(ctx context.Context) { // Stop any running monitors. qm.monitorLock.Lock() defer qm.monitorLock.Unlock() + // Mark as not running so that no new monitors can be started. + // Not doing this here could cause goroutine leaks and deadlocks since it would make it possible for startMonitors + // to proceed and start new monitors after stopMonitors has been called. + qm.running = false monitors := qm.monitors stopped := 0 for _, monitor := range monitors { @@ -341,6 +346,7 @@ func (qm *QuotaMonitor) Run(ctx context.Context) { close(monitor.stopCh) } } + qm.monitors = nil qm.monitorWG.Wait() logger.Info("QuotaMonitor stopped monitors", "stopped", stopped, "total", len(monitors)) }