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()
lock.RLock()
if instance, ok := instances[name]; ok {
defer lock.RUnlock()
return instance.(T)
}
lock.RUnlock()
lock.Lock()
if instance, ok := instances[name]; ok {
lock.Unlock()
return instance.(T)
}
wait, isPending := pending[name]
if !isPending {
pending[name] = make(chan struct{})
wait = pending[name]
}
lock.Unlock()
if isPending {
<-wait
lock.RLock()
defer lock.RUnlock()
return instances[name].(T)
}
newInstance := constructor()
lock.Lock()
instances[name] = newInstance
close(wait)
delete(pending, name)
log.Trace("Created new singleton", "type", name, "instance", fmt.Sprintf("%+v", newInstance))
lock.Unlock()
return newInstance
}