Files

204 lines
4.4 KiB
Go

package repolock
import "sort"
import "strings"
import "sync"
type Manager struct {
locks sync.Map
}
type lockCandidate struct {
id string
index int
}
func NewManager() *Manager {
return &Manager{}
}
func (m *Manager) Lock(repoID string) func() {
return m.LockWrite(repoID)
}
func (m *Manager) TryLock(repoID string) (func(), bool) {
return m.TryLockWrite(repoID)
}
func (m *Manager) LockRead(repoID string) func() {
var key string
var value any
var actual any
var lock *sync.RWMutex
var ok bool
if m == nil { return func() {} }
key = strings.TrimSpace(repoID)
if key == "" { key = "-" }
value = &sync.RWMutex{}
actual, _ = m.locks.LoadOrStore(key, value)
lock, ok = actual.(*sync.RWMutex)
if !ok || lock == nil { return func() {} }
lock.RLock()
return func() {
lock.RUnlock()
}
}
func (m *Manager) TryLockRead(repoID string) (func(), bool) {
var key string
var value any
var actual any
var lock *sync.RWMutex
var ok bool
if m == nil { return func() {}, true }
key = strings.TrimSpace(repoID)
if key == "" { key = "-" }
value = &sync.RWMutex{}
actual, _ = m.locks.LoadOrStore(key, value)
lock, ok = actual.(*sync.RWMutex)
if !ok || lock == nil { return func() {}, true }
if !lock.TryRLock() { return nil, false }
return func() {
lock.RUnlock()
}, true
}
func (m *Manager) LockWrite(repoID string) func() {
var key string
var value any
var actual any
var lock *sync.RWMutex
var ok bool
if m == nil { return func() {} }
key = strings.TrimSpace(repoID)
if key == "" { key = "-" }
value = &sync.RWMutex{}
actual, _ = m.locks.LoadOrStore(key, value)
lock, ok = actual.(*sync.RWMutex)
if !ok || lock == nil { return func() {} }
lock.Lock()
return func() {
lock.Unlock()
}
}
func (m *Manager) TryLockWrite(repoID string) (func(), bool) {
var key string
var value any
var actual any
var lock *sync.RWMutex
var ok bool
if m == nil { return func() {}, true }
key = strings.TrimSpace(repoID)
if key == "" { key = "-" }
value = &sync.RWMutex{}
actual, _ = m.locks.LoadOrStore(key, value)
lock, ok = actual.(*sync.RWMutex)
if !ok || lock == nil { return func() {}, true }
if !lock.TryLock() { return nil, false }
return func() {
lock.Unlock()
}, true
}
func (m *Manager) LockMany(repoIDs []string) func() {
var ids []string
var seen map[string]bool
var unlocks []func()
var id string
var i int
seen = make(map[string]bool)
for i = 0; i < len(repoIDs); i++ {
id = strings.TrimSpace(repoIDs[i])
if id == "" || seen[id] { continue }
seen[id] = true
ids = append(ids, id)
}
sort.Strings(ids)
for i = 0; i < len(ids); i++ {
unlocks = append(unlocks, m.Lock(ids[i]))
}
return func() {
for i = len(unlocks) - 1; i >= 0; i-- {
if unlocks[i] != nil { unlocks[i]() }
}
}
}
func (m *Manager) TryLockMany(repoIDs []string) (func(), bool) {
var ids []string
var seen map[string]bool
var unlocks []func()
var unlock func()
var ok bool
var id string
var i int
seen = make(map[string]bool)
for i = 0; i < len(repoIDs); i++ {
id = strings.TrimSpace(repoIDs[i])
if id == "" || seen[id] { continue }
seen[id] = true
ids = append(ids, id)
}
sort.Strings(ids)
for i = 0; i < len(ids); i++ {
unlock, ok = m.TryLock(ids[i])
if !ok {
for i = len(unlocks) - 1; i >= 0; i-- {
if unlocks[i] != nil { unlocks[i]() }
}
return nil, false
}
unlocks = append(unlocks, unlock)
}
return func() {
for i = len(unlocks) - 1; i >= 0; i-- {
if unlocks[i] != nil { unlocks[i]() }
}
}, true
}
func (m *Manager) TryLockManyIndexed(repoIDs []string) (func(), int, bool) {
var candidates []lockCandidate
var seen map[string]bool
var unlocks []func()
var unlock func()
var candidate lockCandidate
var ok bool
var id string
var i int
seen = make(map[string]bool)
for i = 0; i < len(repoIDs); i++ {
id = strings.TrimSpace(repoIDs[i])
if id == "" || seen[id] { continue }
seen[id] = true
candidates = append(candidates, lockCandidate{id: id, index: i})
}
sort.Slice(candidates, func(i int, j int) bool {
return candidates[i].id < candidates[j].id
})
for i = 0; i < len(candidates); i++ {
candidate = candidates[i]
unlock, ok = m.TryLock(candidate.id)
if !ok {
for i = len(unlocks) - 1; i >= 0; i-- {
if unlocks[i] != nil { unlocks[i]() }
}
return nil, candidate.index, false
}
unlocks = append(unlocks, unlock)
}
return func() {
for i = len(unlocks) - 1; i >= 0; i-- {
if unlocks[i] != nil { unlocks[i]() }
}
}, -1, true
}