132 lines
2.6 KiB
Go
132 lines
2.6 KiB
Go
|
package locks
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"sync"
|
||
|
"time"
|
||
|
)
|
||
|
|
||
|
// MultipleLock is the main interface for multiLock based on key
|
||
|
type MultipleLock interface {
|
||
|
Lock(key string)
|
||
|
RLock(key string)
|
||
|
Unlock(key string)
|
||
|
RUnlock(key string)
|
||
|
}
|
||
|
|
||
|
func NewMultipleLock() MultipleLock {
|
||
|
return &multiLock{
|
||
|
locks: make(map[string]*itemLock),
|
||
|
mu: sync.Mutex{},
|
||
|
}
|
||
|
}
|
||
|
|
||
|
type itemLock struct {
|
||
|
lk *sync.RWMutex
|
||
|
cnt int64
|
||
|
}
|
||
|
|
||
|
// multiLock is an optimized locking system per locking key
|
||
|
type multiLock struct {
|
||
|
locks map[string]*itemLock
|
||
|
mu sync.Mutex // synchronize reads/writes to locks map
|
||
|
}
|
||
|
|
||
|
func debugPrint(format string, a ...interface{}) {
|
||
|
debugEnabled := false
|
||
|
if debugEnabled {
|
||
|
a = append(a, time.Now().Format("04:05.000000"))
|
||
|
fmt.Printf(format+" at %s\n", a...)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (ml *multiLock) Lock(key string) {
|
||
|
debugPrint("mutex requested %s", key)
|
||
|
ml.mu.Lock()
|
||
|
debugPrint("mutex acquired %s", key)
|
||
|
|
||
|
itmLock, exists := ml.locks[key]
|
||
|
if !exists {
|
||
|
debugPrint("new lock created %s", key)
|
||
|
itmLock = &itemLock{&sync.RWMutex{}, 0}
|
||
|
ml.locks[key] = itmLock
|
||
|
}
|
||
|
itmLock.cnt++
|
||
|
debugPrint("releasing mutex %s", key)
|
||
|
ml.mu.Unlock()
|
||
|
|
||
|
debugPrint("Lock requested %s", key)
|
||
|
itmLock.lk.Lock()
|
||
|
debugPrint("Lock acquired %s", key)
|
||
|
}
|
||
|
|
||
|
func (ml *multiLock) RLock(key string) {
|
||
|
debugPrint("mutex requested %s", key)
|
||
|
ml.mu.Lock()
|
||
|
debugPrint("mutex acquired %s", key)
|
||
|
|
||
|
itmLock, exists := ml.locks[key]
|
||
|
if !exists {
|
||
|
debugPrint("new lock created for %s", key)
|
||
|
itmLock = &itemLock{&sync.RWMutex{}, 0}
|
||
|
ml.locks[key] = itmLock
|
||
|
}
|
||
|
itmLock.cnt++
|
||
|
debugPrint("releasing mutex %s", key)
|
||
|
ml.mu.Unlock()
|
||
|
|
||
|
debugPrint("RLock requested %s", key)
|
||
|
itmLock.lk.RLock()
|
||
|
debugPrint("RLock acquired %s", key)
|
||
|
}
|
||
|
|
||
|
func (ml *multiLock) Unlock(key string) {
|
||
|
debugPrint("mutex requested %s", key)
|
||
|
ml.mu.Lock()
|
||
|
debugPrint("mutex acquired %s", key)
|
||
|
|
||
|
itmLock, exists := ml.locks[key]
|
||
|
if !exists {
|
||
|
panic("sync Unlock of non existent lock!!")
|
||
|
}
|
||
|
|
||
|
debugPrint("Unlock %s", key)
|
||
|
itmLock.lk.Unlock()
|
||
|
itmLock.cnt--
|
||
|
if itmLock.cnt == 0 {
|
||
|
debugPrint("delete lock %s", key)
|
||
|
delete(ml.locks, key)
|
||
|
}
|
||
|
if itmLock.cnt < 0 {
|
||
|
panic("sync Unlock of free Lock!!")
|
||
|
}
|
||
|
|
||
|
debugPrint("releasing mutex %s", key)
|
||
|
ml.mu.Unlock()
|
||
|
}
|
||
|
|
||
|
func (ml *multiLock) RUnlock(key string) {
|
||
|
debugPrint("mutex requested %s", key)
|
||
|
ml.mu.Lock()
|
||
|
debugPrint("mutex acquired %s", key)
|
||
|
|
||
|
itmLock, exists := ml.locks[key]
|
||
|
if !exists {
|
||
|
panic("sync Unlock of non existent lock!!")
|
||
|
}
|
||
|
|
||
|
debugPrint("RUnlock %s", key)
|
||
|
itmLock.lk.RUnlock()
|
||
|
itmLock.cnt--
|
||
|
if itmLock.cnt == 0 {
|
||
|
debugPrint("delete lock %s", key)
|
||
|
delete(ml.locks, key)
|
||
|
}
|
||
|
if itmLock.cnt < 0 {
|
||
|
panic("sync Unlock of free Lock!!")
|
||
|
}
|
||
|
|
||
|
debugPrint("releasing mutex %s", key)
|
||
|
ml.mu.Unlock()
|
||
|
}
|