// Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package globallock import ( "context" "sync" "time" ) type memoryLocker struct { locks sync.Map } var _ Locker = &memoryLocker{} func NewMemoryLocker() Locker { return &memoryLocker{} } func (l *memoryLocker) Lock(ctx context.Context, key string) (ReleaseFunc, error) { if l.tryLock(key) { releaseOnce := sync.Once{} return func() { releaseOnce.Do(func() { l.locks.Delete(key) }) }, nil } ticker := time.NewTicker(time.Millisecond * 100) defer ticker.Stop() for { select { case <-ctx.Done(): return func() {}, ctx.Err() case <-ticker.C: if l.tryLock(key) { releaseOnce := sync.Once{} return func() { releaseOnce.Do(func() { l.locks.Delete(key) }) }, nil } } } } func (l *memoryLocker) TryLock(_ context.Context, key string) (bool, ReleaseFunc, error) { if l.tryLock(key) { releaseOnce := sync.Once{} return true, func() { releaseOnce.Do(func() { l.locks.Delete(key) }) }, nil } return false, func() {}, nil } func (l *memoryLocker) tryLock(key string) bool { _, loaded := l.locks.LoadOrStore(key, struct{}{}) return !loaded }