From cac8d98357063ea4ab27c3b4e56c87c0807cecf1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Poyraz=20K=C3=BC=C3=A7=C3=BCkarslan?= <83272398+PoyrazK@users.noreply.github.com> Date: Thu, 7 May 2026 21:42:39 +0300 Subject: [PATCH] fix: run libvirt Connect synchronously to prevent goroutine leak The r.conn.Connect() C binding is a blocking call that cannot be interrupted by context cancellation. Running it in a goroutine added complexity without benefit - if context fires during Connect(), the goroutine continued until Connect() returned anyway. Run Connect synchronously with context check before starting. This eliminates goroutine leak while keeping the early-return behavior when context is already cancelled. Fixes #286 --- internal/repositories/libvirt/real_client.go | 53 ++++---------------- 1 file changed, 10 insertions(+), 43 deletions(-) diff --git a/internal/repositories/libvirt/real_client.go b/internal/repositories/libvirt/real_client.go index 0b7f3f073..a8126ea73 100644 --- a/internal/repositories/libvirt/real_client.go +++ b/internal/repositories/libvirt/real_client.go @@ -5,7 +5,6 @@ import ( "context" "fmt" "math" - "sync" "github.com/digitalocean/go-libvirt" ) @@ -16,59 +15,27 @@ type RealLibvirtClient struct { } func (r *RealLibvirtClient) Connect(ctx context.Context) error { - errChan := make(chan error, 1) - done := make(chan struct{}) - once := sync.Once{} - go func() { - select { - case <-done: - return - default: - } - err := r.conn.Connect() - select { - case errChan <- err: - case <-done: - case <-ctx.Done(): - } - }() - + // Check context before starting - if cancelled, return early select { case <-ctx.Done(): - once.Do(func() { close(done) }) return ctx.Err() - case err := <-errChan: - once.Do(func() { close(done) }) - return err + default: } + // r.conn.Connect() is a blocking C call; we run it synchronously since + // it cannot be interrupted anyway. A goroutine wouldn't help cancellation. + return r.conn.Connect() } func (r *RealLibvirtClient) ConnectToURI(ctx context.Context, uri string) error { - errChan := make(chan error, 1) - done := make(chan struct{}) - once := sync.Once{} - go func() { - select { - case <-done: - return - default: - } - err := r.conn.ConnectToURI(libvirt.ConnectURI(uri)) - select { - case errChan <- err: - case <-done: - case <-ctx.Done(): - } - }() - + // Check context before starting - if cancelled, return early select { case <-ctx.Done(): - once.Do(func() { close(done) }) return ctx.Err() - case err := <-errChan: - once.Do(func() { close(done) }) - return err + default: } + // r.conn.ConnectToURI() is a blocking C call; run it synchronously + // since it cannot be interrupted anyway + return r.conn.ConnectToURI(libvirt.ConnectURI(uri)) } func (r *RealLibvirtClient) Close() error {