mirror of
https://github.com/ncruces/go-sqlite3.git
synced 2026-01-12 14:09:13 +00:00
VFS locking.
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user