Skip to content

Instantly share code, notes, and snippets.

@RuairiSpain
Created April 2, 2024 17:55
Show Gist options
  • Save RuairiSpain/91d6e4989feffe7392c2dad9d9fc3b5e to your computer and use it in GitHub Desktop.
Save RuairiSpain/91d6e4989feffe7392c2dad9d9fc3b5e to your computer and use it in GitHub Desktop.
Go Lock Arbitrator
package arbitrator
import (
"sync"
)
// LockArbitrator manages multiple locks and their waitlists.
type LockArbitrator struct {
locks map[string]*lockState
mu sync.RWMutex // Use RWMutex for read/write locking.
}
// lockState represents the state of a single lock.
type lockState struct {
mutex sync.Mutex
cond *sync.Cond
isLocked bool
owner *string // Owner ID of the lock (nil if no owner).
}
// New creates a new LockArbitrator.
func New() *LockArbitrator {
return &LockArbitrator{
locks: make(map[string]*lockState),
}
}
// RequestLock attempts to acquire a lock for the client, identified by ownerID.
// Returns true if lock is acquired, false if client must wait.
func (la *LockArbitrator) RequestLock(key string, ownerID *string) bool {
la.mu.Lock() // Acquire a write lock.
defer la.mu.Unlock()
l, exists := la.locks[key]
if !exists {
l = &lockState{
mutex: sync.Mutex{},
cond: sync.NewCond(&sync.Mutex{}),
owner: nil,
}
la.locks[key] = l
}
if l.isLocked {
// Lock is already held, must wait.
return false
}
// Acquire the lock and set the owner.
l.isLocked = true
l.owner = ownerID
return true
}
// ReleaseLock releases the lock and notifies one waiting client, if any.
func (la *LockArbitrator) ReleaseLock(key string) {
la.mu.Lock() // Acquire a write lock.
l, exists := la.locks[key]
if !exists {
la.mu.Unlock()
return // No such lock exists, nothing to do.
}
if !l.isLocked {
la.mu.Unlock()
return // Lock is not held, nothing to do.
}
l.isLocked = false
l.owner = nil // Clear the owner.
la.mu.Unlock() // Unlock before signaling to prevent deadlock.
l.cond.Broadcast() // Use Broadcast in case multiple waiters are relevant.
}
// GetLockedItems returns a map of locked items and their owners.
// Uses a read lock to allow concurrent access by readers.
func (la *LockArbitrator) GetLockedItems() map[string]*string {
la.mu.RLock() // Acquire a read lock.
defer la.mu.RUnlock()
lockedItems := make(map[string]*string)
for key, lockState := range la.locks {
if lockState.isLocked {
lockedItems[key] = lockState.owner
}
}
return lockedItems
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment