aboutsummaryrefslogtreecommitdiffstats
path: root/modules/globallock/memory_locker.go
blob: 3f818d8d43929ca98488a792352b85c8086241fa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
// 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
}