storj/satellite/overlay/keylock.go

65 lines
1.7 KiB
Go

// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information
package overlay
import (
"sync"
"storj.io/storj/pkg/storj"
)
// KeyLock provides per-key RW locking. Locking is key-specific, meaning lock
// contention only exists for locking calls using the same key parameter. As
// with all locks, do not call Unlock() or RUnlock() before a corresponding
// Lock() or RLock() call.
//
// Note on memory usage: internally KeyLock lazily and atomically creates a
// separate sync.RWMutex for each key. To maintain synchronization guarantees,
// these interal mutexes are not freed until the entire KeyLock instance is
// freed.
type KeyLock struct {
locksMu sync.Mutex
locks map[storj.NodeID]*sync.RWMutex
}
// UnlockFunc is the function to unlock the associated successful lock
type UnlockFunc func()
// NewKeyLock create a new KeyLock
func NewKeyLock() *KeyLock {
return &KeyLock{
locks: make(map[storj.NodeID]*sync.RWMutex),
}
}
// Lock the provided key. Returns the unlock function.
func (l *KeyLock) Lock(nodeID storj.NodeID) UnlockFunc {
lock := l.getLock(nodeID)
lock.Lock()
return lock.Unlock
}
// RLock the provided key. Returns the unlock function.
func (l *KeyLock) RLock(nodeID storj.NodeID) UnlockFunc {
lock := l.getLock(nodeID)
lock.RLock()
return lock.RUnlock
}
// getLock will atomically load the RWMutex for this key. If one does not yet
// exist, it will be lazily and atomically created. The resulting RWMutex is
// returned.
func (l *KeyLock) getLock(nodeID storj.NodeID) *sync.RWMutex {
l.locksMu.Lock()
defer l.locksMu.Unlock()
lo, ok := l.locks[nodeID]
if ok {
return lo
}
mu := &sync.RWMutex{}
l.locks[nodeID] = mu
return mu
}