From 1793e43589ae98be641e12b28ab64780ddddf08b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Tue, 9 Jun 2026 15:36:10 -0400 Subject: [PATCH] incus/client: Fix panic when cancelling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #3471 Signed-off-by: Stéphane Graber --- client/operations.go | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/client/operations.go b/client/operations.go index e24b1c0cf0..932c6a1532 100644 --- a/client/operations.go +++ b/client/operations.go @@ -22,7 +22,15 @@ type operation struct { handlerLock sync.Mutex skipListener bool - chActive chan bool + chActive chan bool + chActiveOnce sync.Once +} + +// closeChActive closes the chActive channel exactly once. +func (op *operation) closeChActive() { + op.chActiveOnce.Do(func() { + close(op.chActive) + }) } // AddHandler adds a function to be called whenever an event is received. @@ -162,6 +170,18 @@ func (op *operation) WaitContext(ctx context.Context) error { select { case <-ctx.Done(): + // Tear down the listener, cancel the server-side operation and unblock the monitor. + op.handlerLock.Lock() + if op.listener != nil { + op.listener.Disconnect() + op.listener = nil + } + + op.handlerLock.Unlock() + + _ = op.Cancel() + op.closeChActive() + return ctx.Err() case <-op.chActive: } @@ -231,14 +251,14 @@ func (op *operation) setupListener() error { if op.StatusCode.IsFinal() { op.listener.Disconnect() op.listener = nil - close(op.chActive) + op.closeChActive() return } }) if err != nil { op.listener.Disconnect() op.listener = nil - close(op.chActive) + op.closeChActive() close(chReady) return err @@ -266,7 +286,7 @@ func (op *operation) setupListener() error { op.handlerLock.Lock() if op.listener != nil { op.Err = listener.err.Error() - close(op.chActive) + op.closeChActive() } op.handlerLock.Unlock() @@ -280,7 +300,7 @@ func (op *operation) setupListener() error { if err != nil { op.listener.Disconnect() op.listener = nil - close(op.chActive) + op.closeChActive() close(chReady) return err @@ -290,7 +310,7 @@ func (op *operation) setupListener() error { if op.StatusCode.IsFinal() { op.listener.Disconnect() op.listener = nil - close(op.chActive) + op.closeChActive() close(chReady) if op.Err != "" {