mirror of
https://github.com/go-gitea/gitea
synced 2024-11-02 00:04:25 +00:00
81 lines
1.7 KiB
Go
81 lines
1.7 KiB
Go
|
// 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) (context.Context, ReleaseFunc, error) {
|
||
|
originalCtx := ctx
|
||
|
|
||
|
if l.tryLock(key) {
|
||
|
ctx, cancel := context.WithCancelCause(ctx)
|
||
|
releaseOnce := sync.Once{}
|
||
|
return ctx, func() context.Context {
|
||
|
releaseOnce.Do(func() {
|
||
|
l.locks.Delete(key)
|
||
|
cancel(ErrLockReleased)
|
||
|
})
|
||
|
return originalCtx
|
||
|
}, nil
|
||
|
}
|
||
|
|
||
|
ticker := time.NewTicker(time.Millisecond * 100)
|
||
|
defer ticker.Stop()
|
||
|
for {
|
||
|
select {
|
||
|
case <-ctx.Done():
|
||
|
return ctx, func() context.Context { return originalCtx }, ctx.Err()
|
||
|
case <-ticker.C:
|
||
|
if l.tryLock(key) {
|
||
|
ctx, cancel := context.WithCancelCause(ctx)
|
||
|
releaseOnce := sync.Once{}
|
||
|
return ctx, func() context.Context {
|
||
|
releaseOnce.Do(func() {
|
||
|
l.locks.Delete(key)
|
||
|
cancel(ErrLockReleased)
|
||
|
})
|
||
|
return originalCtx
|
||
|
}, nil
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (l *memoryLocker) TryLock(ctx context.Context, key string) (bool, context.Context, ReleaseFunc, error) {
|
||
|
originalCtx := ctx
|
||
|
|
||
|
if l.tryLock(key) {
|
||
|
ctx, cancel := context.WithCancelCause(ctx)
|
||
|
releaseOnce := sync.Once{}
|
||
|
return true, ctx, func() context.Context {
|
||
|
releaseOnce.Do(func() {
|
||
|
cancel(ErrLockReleased)
|
||
|
l.locks.Delete(key)
|
||
|
})
|
||
|
return originalCtx
|
||
|
}, nil
|
||
|
}
|
||
|
|
||
|
return false, ctx, func() context.Context { return originalCtx }, nil
|
||
|
}
|
||
|
|
||
|
func (l *memoryLocker) tryLock(key string) bool {
|
||
|
_, loaded := l.locks.LoadOrStore(key, struct{}{})
|
||
|
return !loaded
|
||
|
}
|