Files
kopia/internal/cache/mutex_map.go
Jarek Kowalski fe55dcb6a2 feat(repository): added hard size limit to the on-disk cache (#3238)
* test(providers): added capacity limits to blobtesting.mapStorage

* refactor(general): added mutex map which dynamically allocates and releases named mutexes

* refactor(repository): refactored cache cleanup and limit enforcement

* refactor(repository): plumb through cache size limits in the repository

* feat(cli): added CLI options to set cache size limits

* unified flag setting and field naming

* Update cli/command_cache_set.go

Co-authored-by: Shikhar Mall <mall.shikhar.in@gmail.com>

* pr feedback

---------

Co-authored-by: Shikhar Mall <mall.shikhar.in@gmail.com>
2023-08-24 09:38:56 -07:00

96 lines
1.7 KiB
Go

package cache
import (
"sync"
)
// manages a map of RWMutexes indexed by string keys
// mutexes are allocated on demand and released when no longer needed.
type mutexMap struct {
mu sync.Mutex
// +checklocks:mu
entries map[string]*mutexMapEntry
}
type mutexMapEntry struct {
mut *sync.RWMutex
refCount int
}
// +checklocksignore.
func (m *mutexMap) exclusiveLock(key string) {
m.getMutexAndAddRef(key).Lock()
}
func (m *mutexMap) tryExclusiveLock(key string) bool {
if !m.getMutexAndAddRef(key).TryLock() { // +checklocksignore
m.getMutexAndReleaseRef(key)
return false
}
return true
}
func (m *mutexMap) exclusiveUnlock(key string) {
m.getMutexAndReleaseRef(key).Unlock() // +checklocksignore
}
// +checklocksignore.
func (m *mutexMap) sharedLock(key string) {
m.getMutexAndAddRef(key).RLock()
}
func (m *mutexMap) trySharedLock(key string) bool {
if !m.getMutexAndAddRef(key).TryRLock() { // +checklocksignore
m.getMutexAndReleaseRef(key)
return false
}
return true
}
func (m *mutexMap) sharedUnlock(key string) {
m.getMutexAndReleaseRef(key).RUnlock() // +checklocksignore
}
func (m *mutexMap) getMutexAndAddRef(key string) *sync.RWMutex {
m.mu.Lock()
defer m.mu.Unlock()
ent := m.entries[key]
if ent == nil {
if m.entries == nil {
m.entries = make(map[string]*mutexMapEntry)
}
ent = &mutexMapEntry{
mut: &sync.RWMutex{},
}
m.entries[key] = ent
}
ent.refCount++
return ent.mut
}
func (m *mutexMap) getMutexAndReleaseRef(key string) *sync.RWMutex {
m.mu.Lock()
defer m.mu.Unlock()
if m.entries == nil {
panic("attempted to call unlock without a lock")
}
ent := m.entries[key]
ent.refCount--
if ent.refCount == 0 {
delete(m.entries, key)
}
return ent.mut
}