package singleton
import (
"fmt"
"reflect"
"sync"
"github.com/navidrome/navidrome/log"
)
var (
instances = map[string]any{}
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
lock.Unlock()
// 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]
// 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
return instances[name].(T)
// We're responsible for creating the instance
newInstance := constructor()
// Store it and signal other goroutines
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))
return newInstance