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 }