VFS locking.

This commit is contained in:
Nuno Cruces
2023-05-26 11:04:48 +01:00
parent 7ca9d79424
commit f1c46db512
3 changed files with 132 additions and 51 deletions

View File

@@ -2,7 +2,9 @@ package sqlite3vfs
import (
"io"
"runtime"
"sync"
"time"
)
// A MemoryVFS is a [VFS] for memory databases.
@@ -42,14 +44,19 @@ func (vfs MemoryVFS) FullPathname(name string) (string, error) {
const memSectorSize = 65536
type MemoryDB struct {
mtx sync.RWMutex
size int64
data []*[memSectorSize]byte
mtx sync.Mutex
locker sync.Mutex
pending *memoryFile
reserved *memoryFile
shared int
}
type memoryFile struct {
*MemoryDB
locked bool
lock LockLevel
readOnly bool
}
@@ -58,6 +65,9 @@ func (m *memoryFile) Close() error {
}
func (m *memoryFile) ReadAt(b []byte, off int64) (n int, err error) {
m.mtx.RLock()
defer m.mtx.RUnlock()
if off >= m.size {
return 0, io.EOF
}
@@ -71,6 +81,9 @@ func (m *memoryFile) ReadAt(b []byte, off int64) (n int, err error) {
}
func (m *memoryFile) WriteAt(b []byte, off int64) (n int, err error) {
m.mtx.Lock()
defer m.mtx.Unlock()
base := off / memSectorSize
rest := off % memSectorSize
if base >= int64(len(m.data)) {
@@ -84,6 +97,12 @@ func (m *memoryFile) WriteAt(b []byte, off int64) (n int, err error) {
}
func (m *memoryFile) Truncate(size int64) error {
m.mtx.Lock()
defer m.mtx.Unlock()
return m.truncate(size)
}
func (m *memoryFile) truncate(size int64) error {
if size < m.size {
base := size / memSectorSize
rest := size % memSectorSize
@@ -107,37 +126,93 @@ func (*memoryFile) Sync(flag SyncFlag) error {
}
func (m *memoryFile) Size() (int64, error) {
m.mtx.RLock()
defer m.mtx.RUnlock()
return m.size, nil
}
func (m *memoryFile) Lock(lock LockLevel) error {
if m.lock >= lock {
return nil
}
if m.readOnly && lock >= LOCK_RESERVED {
return _IOERR_LOCK
}
if m.locked || m.mtx.TryLock() {
m.locked = true
return nil
m.locker.Lock()
defer m.locker.Unlock()
deadline := time.Now().Add(time.Millisecond)
switch lock {
case LOCK_SHARED:
for m.pending != nil {
if time.Now().After(deadline) {
return _BUSY
}
m.locker.Unlock()
runtime.Gosched()
m.locker.Lock()
}
m.shared++
case LOCK_RESERVED:
if m.reserved != nil {
return _BUSY
}
m.reserved = m
case LOCK_EXCLUSIVE:
if m.lock < LOCK_PENDING {
if m.pending != nil {
return _BUSY
}
m.lock = LOCK_PENDING
m.pending = m
}
for m.shared > 1 {
if time.Now().After(deadline) {
return _BUSY
}
m.locker.Unlock()
runtime.Gosched()
m.locker.Lock()
}
}
return _BUSY
m.lock = lock
return nil
}
func (m *memoryFile) Unlock(lock LockLevel) error {
if m.locked && lock == LOCK_NONE {
m.locked = false
m.mtx.Unlock()
if m.lock <= lock {
return nil
}
m.locker.Lock()
defer m.locker.Unlock()
if m.pending == m {
m.pending = nil
}
if m.reserved == m {
m.reserved = nil
}
if lock < LOCK_SHARED {
m.shared--
}
m.lock = lock
return nil
}
func (m *memoryFile) CheckReservedLock() (bool, error) {
if m.locked {
if m.lock >= LOCK_RESERVED {
return true, nil
}
if m.mtx.TryLock() {
m.mtx.Unlock()
return true, nil
}
return false, nil
m.locker.Lock()
defer m.locker.Unlock()
return m.reserved != nil, nil
}
func (*memoryFile) SectorSize() int {
@@ -152,12 +227,18 @@ func (*memoryFile) DeviceCharacteristics() DeviceCharacteristic {
}
func (m *memoryFile) SizeHint(size int64) error {
m.mtx.Lock()
defer m.mtx.Unlock()
if size > m.size {
return m.Truncate(size)
return m.truncate(size)
}
return nil
}
func (m *memoryFile) LockState() LockLevel {
return m.lock
}
func clear(b []byte) {
for i := range b {
b[i] = 0