204 lines
4.4 KiB
Go
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
|
|
}
|