Created
April 2, 2024 17:55
-
-
Save RuairiSpain/91d6e4989feffe7392c2dad9d9fc3b5e to your computer and use it in GitHub Desktop.
Go Lock Arbitrator
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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