os_dotlk.go

  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}