diff options
author | 2021-09-12 10:10:24 +0100 | |
---|---|---|
committer | 2021-09-12 10:10:24 +0100 | |
commit | f6492d12d948507021bbe934de94e87e20464c01 (patch) | |
tree | 6705d6ef6f3c4d70f3b3ebc77c2960d8e508cf37 /vendor/git.iim.gay/grufwub/go-mutexes/map.go | |
parent | Merge pull request #213 from superseriousbusiness/alpine+node_upstep (diff) | |
parent | fix keys used to access storage items (diff) | |
download | gotosocial-f6492d12d948507021bbe934de94e87e20464c01.tar.xz |
Merge pull request #214 from NyaaaWhatsUpDoc/improvement/update-storage-library
add git.iim.gay/grufwub/go-store for storage backend, replacing blob.Storage
Diffstat (limited to 'vendor/git.iim.gay/grufwub/go-mutexes/map.go')
-rw-r--r-- | vendor/git.iim.gay/grufwub/go-mutexes/map.go | 113 |
1 files changed, 113 insertions, 0 deletions
diff --git a/vendor/git.iim.gay/grufwub/go-mutexes/map.go b/vendor/git.iim.gay/grufwub/go-mutexes/map.go new file mode 100644 index 000000000..1e5b2781a --- /dev/null +++ b/vendor/git.iim.gay/grufwub/go-mutexes/map.go @@ -0,0 +1,113 @@ +package mutexes + +import ( + "sync" +) + +// MutexMap is a structure that allows having a map of self-evicting mutexes +// by key. You do not need to worry about managing the contents of the map, +// only requesting RLock/Lock for keys, and ensuring to call the returned +// unlock functions. +type MutexMap interface { + // Lock acquires a mutex lock for supplied key, returning an Unlock function + Lock(key string) (unlock func()) + + // RLock acquires a mutex read lock for supplied key, returning an RUnlock function + RLock(key string) (runlock func()) +} + +type mutexMap struct { + // NOTE: + // Individual keyed mutexes should ONLY ever + // be locked within the protection of the outer + // mapMu lock. If you lock these outside the + // protection of this, there is a chance for + // deadlocks + + mus map[string]RWMutex + mapMu sync.Mutex + pool sync.Pool +} + +// NewMap returns a new MutexMap instance based on supplied +// RWMutex allocator function, nil implies use default +func NewMap(newFn func() RWMutex) MutexMap { + if newFn == nil { + newFn = NewRW + } + return &mutexMap{ + mus: make(map[string]RWMutex), + mapMu: sync.Mutex{}, + pool: sync.Pool{ + New: func() interface{} { + return newFn() + }, + }, + } +} + +func (mm *mutexMap) evict(key string, mu RWMutex) { + // Acquire map lock + mm.mapMu.Lock() + + // Toggle mutex lock to + // ensure it is unused + unlock := mu.Lock() + unlock() + + // Delete mutex key + delete(mm.mus, key) + mm.mapMu.Unlock() + + // Release to pool + mm.pool.Put(mu) +} + +// GetRLock acquires a mutex read lock for supplied key, returning an RUnlock function +func (mm *mutexMap) RLock(key string) func() { + return mm.getLock(key, func(mu RWMutex) func() { + return mu.RLock() + }) +} + +// GetLock acquires a mutex lock for supplied key, returning an Unlock function +func (mm *mutexMap) Lock(key string) func() { + return mm.getLock(key, func(mu RWMutex) func() { + return mu.Lock() + }) +} + +func (mm *mutexMap) getLock(key string, doLock func(RWMutex) func()) func() { + // Get map lock + mm.mapMu.Lock() + + // Look for mutex + mu, ok := mm.mus[key] + if ok { + // Lock and return + // its unlocker func + unlock := doLock(mu) + mm.mapMu.Unlock() + return unlock + } + + // Note: even though the mutex data structure is + // small, benchmarking does actually show that pooled + // alloc of mutexes here is faster + + // Acquire mu + add + mu = mm.pool.Get().(RWMutex) + mm.mus[key] = mu + + // Lock mutex + unlock map + unlockFn := doLock(mu) + mm.mapMu.Unlock() + + return func() { + // Unlock mutex + unlockFn() + + // Release function + go mm.evict(key, mu) + } +} |