From 73d546235eae99b4805c3b702483dad09f185877 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 28 Feb 2026 06:51:41 +0000 Subject: [PATCH] detect: replace sync.Once with atomic.Pointer for race-safe caching and Reset() Co-authored-by: taigrr <8261498+taigrr@users.noreply.github.com> --- detect/detect.go | 48 +++++++++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/detect/detect.go b/detect/detect.go index 58e7fed..fa100aa 100644 --- a/detect/detect.go +++ b/detect/detect.go @@ -3,42 +3,44 @@ package detect import ( "os/exec" - "sync" + "sync/atomic" "github.com/gogrlx/snack" ) -var ( - defaultOnce sync.Once - defaultMgr snack.Manager - defaultErr error -) +type detected struct { + mgr snack.Manager + err error +} + +var result atomic.Pointer[detected] // Default returns the first available package manager on the system. -// The result is cached after the first call. +// The result is cached after the first successful detection. // Returns ErrManagerNotFound if no supported manager is detected. +// Multiple goroutines may probe simultaneously on the first call; this is +// harmless because Available() is read-only and all goroutines store +// equivalent results. func Default() (snack.Manager, error) { - defaultOnce.Do(func() { - for _, fn := range candidates() { - m := fn() - if m.Available() { - defaultMgr = m - return - } + if r := result.Load(); r != nil { + return r.mgr, r.err + } + for _, fn := range candidates() { + m := fn() + if m.Available() { + result.Store(&detected{mgr: m}) + return m, nil } - defaultErr = snack.ErrManagerNotFound - }) - return defaultMgr, defaultErr + } + r := &detected{err: snack.ErrManagerNotFound} + result.Store(r) + return r.mgr, r.err } // Reset clears the cached result of Default(), forcing re-detection on the -// next call. This is intended for use in tests or dynamic environments where -// the available package managers may change. -// Reset is not safe to call concurrently with Default(). +// next call. This is safe to call concurrently with Default(). func Reset() { - defaultOnce = sync.Once{} - defaultMgr = nil - defaultErr = nil + result.Store(nil) } // All returns all available package managers on the system.