1//go:build sqlite3_dotlk
2
3package vfs
4
5import (
6 "errors"
7 "io/fs"
8 "os"
9 "sync"
10
11 "github.com/ncruces/go-sqlite3/internal/dotlk"
12)
13
14var (
15 // +checklocks:vfsDotLocksMtx
16 vfsDotLocks = map[string]*vfsDotLocker{}
17 vfsDotLocksMtx sync.Mutex
18)
19
20type vfsDotLocker struct {
21 shared int // +checklocks:vfsDotLocksMtx
22 pending *os.File // +checklocks:vfsDotLocksMtx
23 reserved *os.File // +checklocks:vfsDotLocksMtx
24}
25
26func osGetSharedLock(file *os.File) _ErrorCode {
27 vfsDotLocksMtx.Lock()
28 defer vfsDotLocksMtx.Unlock()
29
30 name := file.Name()
31 locker := vfsDotLocks[name]
32 if locker == nil {
33 if err := dotlk.TryLock(name + ".lock"); err != nil {
34 if errors.Is(err, fs.ErrExist) {
35 return _BUSY // Another process has the lock.
36 }
37 return _IOERR_LOCK
38 }
39 locker = &vfsDotLocker{}
40 vfsDotLocks[name] = locker
41 }
42
43 if locker.pending != nil {
44 return _BUSY
45 }
46 locker.shared++
47 return _OK
48}
49
50func osGetReservedLock(file *os.File) _ErrorCode {
51 vfsDotLocksMtx.Lock()
52 defer vfsDotLocksMtx.Unlock()
53
54 name := file.Name()
55 locker := vfsDotLocks[name]
56 if locker == nil {
57 return _IOERR_LOCK
58 }
59
60 if locker.reserved != nil && locker.reserved != file {
61 return _BUSY
62 }
63 locker.reserved = file
64 return _OK
65}
66
67func osGetExclusiveLock(file *os.File, _ *LockLevel) _ErrorCode {
68 vfsDotLocksMtx.Lock()
69 defer vfsDotLocksMtx.Unlock()
70
71 name := file.Name()
72 locker := vfsDotLocks[name]
73 if locker == nil {
74 return _IOERR_LOCK
75 }
76
77 if locker.pending != nil && locker.pending != file {
78 return _BUSY
79 }
80 locker.pending = file
81 if locker.shared > 1 {
82 return _BUSY
83 }
84 return _OK
85}
86
87func osDowngradeLock(file *os.File, _ LockLevel) _ErrorCode {
88 vfsDotLocksMtx.Lock()
89 defer vfsDotLocksMtx.Unlock()
90
91 name := file.Name()
92 locker := vfsDotLocks[name]
93 if locker == nil {
94 return _IOERR_UNLOCK
95 }
96
97 if locker.reserved == file {
98 locker.reserved = nil
99 }
100 if locker.pending == file {
101 locker.pending = nil
102 }
103 return _OK
104}
105
106func osReleaseLock(file *os.File, state LockLevel) _ErrorCode {
107 vfsDotLocksMtx.Lock()
108 defer vfsDotLocksMtx.Unlock()
109
110 name := file.Name()
111 locker := vfsDotLocks[name]
112 if locker == nil {
113 return _IOERR_UNLOCK
114 }
115
116 if locker.shared == 1 {
117 if err := dotlk.Unlock(name + ".lock"); err != nil {
118 return _IOERR_UNLOCK
119 }
120 delete(vfsDotLocks, name)
121 }
122
123 if locker.reserved == file {
124 locker.reserved = nil
125 }
126 if locker.pending == file {
127 locker.pending = nil
128 }
129 locker.shared--
130 return _OK
131}
132
133func osCheckReservedLock(file *os.File) (bool, _ErrorCode) {
134 vfsDotLocksMtx.Lock()
135 defer vfsDotLocksMtx.Unlock()
136
137 name := file.Name()
138 locker := vfsDotLocks[name]
139 if locker == nil {
140 return false, _OK
141 }
142 return locker.reserved != nil, _OK
143}