vendor tsgo
This commit is contained in:
107
tools/tsgo/internal/project/ownercache.go
Normal file
107
tools/tsgo/internal/project/ownercache.go
Normal file
@@ -0,0 +1,107 @@
|
||||
package project
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/microsoft/typescript-go/internal/collections"
|
||||
)
|
||||
|
||||
type ownerCacheEntry[V any] struct {
|
||||
mu sync.Mutex
|
||||
value V
|
||||
owners map[uint64]struct{}
|
||||
}
|
||||
|
||||
// OwnerCache is like RefCountCache, but each entry tracks the set of its
|
||||
// owners instead of a count. We use this to associate extended config cache
|
||||
// entries with each snapshot that contains them, since the same config can
|
||||
// be Acquired multiple times during config parsing while only appearing once in
|
||||
// the ParsedCommandLine's list of extended files. When updating this code, check
|
||||
// if the same changes should be made to RefCountCache as well.
|
||||
type OwnerCache[K comparable, V any, LoadArgs any] struct {
|
||||
entries collections.SyncMap[K, *ownerCacheEntry[V]]
|
||||
|
||||
isExpired func(K, V, LoadArgs) bool
|
||||
parse func(K, LoadArgs) V
|
||||
}
|
||||
|
||||
func NewOwnerCache[K comparable, V any, LoadArgs any](
|
||||
parse func(K, LoadArgs) V,
|
||||
isExpired func(K, V, LoadArgs) bool,
|
||||
) *OwnerCache[K, V, LoadArgs] {
|
||||
return &OwnerCache[K, V, LoadArgs]{
|
||||
isExpired: isExpired,
|
||||
parse: parse,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *OwnerCache[K, V, LoadArgs]) LoadAndAcquire(identity K, owner uint64, loadArgs LoadArgs) V {
|
||||
entry, loaded := c.loadOrStoreLockedEntry(identity)
|
||||
defer entry.mu.Unlock()
|
||||
if !loaded || c.isExpired != nil && c.isExpired(identity, entry.value, loadArgs) {
|
||||
entry.value = c.parse(identity, loadArgs)
|
||||
}
|
||||
entry.owners[owner] = struct{}{}
|
||||
return entry.value
|
||||
}
|
||||
|
||||
func (c *OwnerCache[K, V, LoadArgs]) Acquire(identity K, owner uint64, value V) {
|
||||
entry, loaded := c.loadOrStoreLockedEntry(identity)
|
||||
defer entry.mu.Unlock()
|
||||
if !loaded {
|
||||
entry.value = value
|
||||
}
|
||||
entry.owners[owner] = struct{}{}
|
||||
}
|
||||
|
||||
// AddOwner adds an owner to an existing live entry. The entry must exist
|
||||
// and have at least one current owner; callers must ensure the entry is
|
||||
// kept alive (e.g. via snapshot ref counting).
|
||||
func (c *OwnerCache[K, V, LoadArgs]) AddOwner(identity K, owner uint64) {
|
||||
entry, ok := c.entries.Load(identity)
|
||||
if !ok {
|
||||
panic("OwnerCache.AddOwner: entry not found")
|
||||
}
|
||||
entry.mu.Lock()
|
||||
defer entry.mu.Unlock()
|
||||
if len(entry.owners) == 0 {
|
||||
panic("OwnerCache.AddOwner: entry has no owners")
|
||||
}
|
||||
entry.owners[owner] = struct{}{}
|
||||
}
|
||||
|
||||
func (c *OwnerCache[K, V, LoadArgs]) Has(identity K) bool {
|
||||
_, ok := c.entries.Load(identity)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (c *OwnerCache[K, V, LoadArgs]) Release(identity K, owner uint64) {
|
||||
entry, ok := c.entries.Load(identity)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
entry.mu.Lock()
|
||||
defer entry.mu.Unlock()
|
||||
delete(entry.owners, owner)
|
||||
if len(entry.owners) == 0 {
|
||||
c.entries.Delete(identity)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *OwnerCache[K, V, LoadArgs]) loadOrStoreLockedEntry(key K) (*ownerCacheEntry[V], bool) {
|
||||
entry := &ownerCacheEntry[V]{
|
||||
owners: make(map[uint64]struct{}),
|
||||
}
|
||||
entry.mu.Lock()
|
||||
existing, loaded := c.entries.LoadOrStore(key, entry)
|
||||
if loaded {
|
||||
entry.mu.Unlock()
|
||||
existing.mu.Lock()
|
||||
if len(existing.owners) == 0 {
|
||||
existing.mu.Unlock()
|
||||
return c.loadOrStoreLockedEntry(key)
|
||||
}
|
||||
return existing, true
|
||||
}
|
||||
return entry, false
|
||||
}
|
||||
Reference in New Issue
Block a user