update
Some checks failed
Pipeline: Test, Lint, Build / Get version info (push) Has been cancelled
Pipeline: Test, Lint, Build / Lint Go code (push) Has been cancelled
Pipeline: Test, Lint, Build / Test Go code (push) Has been cancelled
Pipeline: Test, Lint, Build / Test JS code (push) Has been cancelled
Pipeline: Test, Lint, Build / Lint i18n files (push) Has been cancelled
Pipeline: Test, Lint, Build / Check Docker configuration (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (darwin/amd64) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (darwin/arm64) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (linux/386) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (linux/amd64) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (linux/arm/v5) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (linux/arm/v6) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (linux/arm/v7) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (linux/arm64) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (windows/386) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (windows/amd64) (push) Has been cancelled
Pipeline: Test, Lint, Build / Push to GHCR (push) Has been cancelled
Pipeline: Test, Lint, Build / Push to Docker Hub (push) Has been cancelled
Pipeline: Test, Lint, Build / Cleanup digest artifacts (push) Has been cancelled
Pipeline: Test, Lint, Build / Build Windows installers (push) Has been cancelled
Pipeline: Test, Lint, Build / Package/Release (push) Has been cancelled
Pipeline: Test, Lint, Build / Upload Linux PKG (push) Has been cancelled
Close stale issues and PRs / stale (push) Has been cancelled
POEditor import / update-translations (push) Has been cancelled

This commit is contained in:
2025-12-08 16:16:23 +01:00
commit c251f174ed
1349 changed files with 194301 additions and 0 deletions

View File

@@ -0,0 +1,69 @@
package singleton
import (
"fmt"
"reflect"
"sync"
"github.com/navidrome/navidrome/log"
)
var (
instances = map[string]interface{}{}
pending = map[string]chan struct{}{}
lock sync.RWMutex
)
func GetInstance[T any](constructor func() T) T {
var v T
name := reflect.TypeOf(v).String()
// First check with read lock
lock.RLock()
if instance, ok := instances[name]; ok {
defer lock.RUnlock()
return instance.(T)
}
lock.RUnlock()
// Now check if someone is already creating this type
lock.Lock()
// Check again with the write lock - someone might have created it
if instance, ok := instances[name]; ok {
lock.Unlock()
return instance.(T)
}
// Check if creation is pending
wait, isPending := pending[name]
if !isPending {
// We'll be the one creating it
pending[name] = make(chan struct{})
wait = pending[name]
}
lock.Unlock()
// If someone else is creating it, wait for them
if isPending {
<-wait // Wait for creation to complete
// Now it should be in the instances map
lock.RLock()
defer lock.RUnlock()
return instances[name].(T)
}
// We're responsible for creating the instance
newInstance := constructor()
// Store it and signal other goroutines
lock.Lock()
instances[name] = newInstance
close(wait) // Signal that creation is complete
delete(pending, name) // Clean up
log.Trace("Created new singleton", "type", name, "instance", fmt.Sprintf("%+v", newInstance))
lock.Unlock()
return newInstance
}