Blocking locks (#144)

This commit is contained in:
Nuno Cruces
2024-09-02 23:59:26 +01:00
committed by GitHub
parent b9b2ff13da
commit f26f1a17a9
10 changed files with 43 additions and 16 deletions

Binary file not shown.

Binary file not shown.

View File

@@ -24,6 +24,7 @@
#define SQLITE_ENABLE_ATOMIC_WRITE
#define SQLITE_ENABLE_BATCH_ATOMIC_WRITE
#define SQLITE_ENABLE_COLUMN_METADATA
#define SQLITE_ENABLE_SETLK_TIMEOUT 2
#define SQLITE_ENABLE_STAT4 1
// We have our own memdb VFS.

View File

@@ -79,16 +79,15 @@ func (f *vfsFile) Lock(lock LockLevel) error {
// A PENDING lock is needed before acquiring an EXCLUSIVE lock.
if f.lock < LOCK_PENDING {
// If we're already RESERVED, we can block indefinitely,
// since only new readers may briefly hold the PENDING lock.
// since only incoming readers may briefly hold the PENDING lock.
if rc := osGetPendingLock(f.File, reserved /* block */); rc != _OK {
return rc
}
f.lock = LOCK_PENDING
}
// We already have PENDING, so we're just waiting for readers to leave.
// If we were RESERVED, we can wait for a little while, before invoking
// the busy handler; we will only do this once.
if rc := osGetExclusiveLock(f.File, reserved /* wait */); rc != _OK {
// We are now PENDING, so we're just waiting for readers to leave.
// If we were RESERVED, we can block for a bit before invoking the busy handler.
if rc := osGetExclusiveLock(f.File, reserved /* block */); rc != _OK {
return rc
}
f.lock = LOCK_EXCLUSIVE

View File

@@ -32,9 +32,9 @@ func osGetPendingLock(file *os.File, block bool) _ErrorCode {
return osWriteLock(file, _PENDING_BYTE, 1, timeout)
}
func osGetExclusiveLock(file *os.File, wait bool) _ErrorCode {
func osGetExclusiveLock(file *os.File, block bool) _ErrorCode {
var timeout time.Duration
if wait {
if block {
timeout = time.Millisecond
}
// Acquire the EXCLUSIVE lock.

View File

@@ -38,9 +38,9 @@ func osGetPendingLock(file *os.File, block bool) _ErrorCode {
return osWriteLock(file, _PENDING_BYTE, 1, timeout)
}
func osGetExclusiveLock(file *os.File, wait bool) _ErrorCode {
func osGetExclusiveLock(file *os.File, block bool) _ErrorCode {
var timeout time.Duration
if wait {
if block {
timeout = time.Millisecond
}

View File

@@ -6,6 +6,7 @@ import (
"context"
"io"
"os"
"time"
"github.com/ncruces/go-sqlite3/internal/util"
"github.com/tetratelabs/wazero/api"
@@ -49,6 +50,7 @@ type vfsShm struct {
path string
regions []*util.MappedRegion
readOnly bool
blocking bool
}
func (s *vfsShm) shmOpen() _ErrorCode {
@@ -76,6 +78,13 @@ func (s *vfsShm) shmOpen() _ErrorCode {
if s.readOnly {
return _READONLY_CANTINIT
}
// Do not use a blocking lock here.
// If the lock cannot be obtained immediately,
// it means some other connection is truncating the file.
// And after it has done so, it will not release its lock,
// but only downgrade it to a shared lock.
// So no point in blocking here.
// The call below to obtain the shared DMS lock may use a blocking lock.
if rc := osWriteLock(s.File, _SHM_DMS, 1, 0); rc != _OK {
return rc
}
@@ -83,7 +92,7 @@ func (s *vfsShm) shmOpen() _ErrorCode {
return _IOERR_SHMOPEN
}
}
if rc := osReadLock(s.File, _SHM_DMS, 1, 0); rc != _OK {
if rc := osReadLock(s.File, _SHM_DMS, 1, time.Millisecond); rc != _OK {
return rc
}
return _OK
@@ -150,13 +159,18 @@ func (s *vfsShm) shmLock(offset, n int32, flags _ShmFlag) _ErrorCode {
panic(util.AssertErr())
}
var timeout time.Duration
if s.blocking {
timeout = time.Millisecond
}
switch {
case flags&_SHM_UNLOCK != 0:
return osUnlock(s.File, _SHM_BASE+int64(offset), int64(n))
case flags&_SHM_SHARED != 0:
return osReadLock(s.File, _SHM_BASE+int64(offset), int64(n), 0)
return osReadLock(s.File, _SHM_BASE+int64(offset), int64(n), timeout)
case flags&_SHM_EXCLUSIVE != 0:
return osWriteLock(s.File, _SHM_BASE+int64(offset), int64(n), 0)
return osWriteLock(s.File, _SHM_BASE+int64(offset), int64(n), timeout)
default:
panic(util.AssertErr())
}
@@ -181,3 +195,7 @@ func (s *vfsShm) shmUnmap(delete bool) {
s.Close()
s.File = nil
}
func (s *vfsShm) shmEnableBlocking(block bool) {
s.blocking = block
}

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4d58c92d45fb60dc2eea461b0e7c1d512cb1dd00deafdfaab3e24b943f714f69
size 475960
oid sha256:95b87ae12a3d5635f00fd3014667caaa89094fa36a23ba9a3bfc3561ef226a03
size 476074

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:90eb053e2a17bd73d8fb17f82c60e595b71018a7880c7b04d7f4b6aae187f5a5
size 489132
oid sha256:41eb60af3e4010d1e8dd4011ff9fa02bf299e15e38c2ce6f8b04569cb5aa6579
size 489289

View File

@@ -243,6 +243,15 @@ func vfsFileControl(ctx context.Context, mod api.Module, pFile uint32, op _Fcntl
return _OK
}
case _FCNTL_LOCK_TIMEOUT:
if file, ok := file.(FileSharedMemory); ok {
if iface, ok := file.SharedMemory().(interface{ shmEnableBlocking(bool) }); ok {
if i := util.ReadUint32(mod, pArg); i == 0 || i == 1 {
iface.shmEnableBlocking(i != 0)
}
}
}
case _FCNTL_PERSIST_WAL:
if file, ok := file.(FilePersistentWAL); ok {
if i := util.ReadUint32(mod, pArg); int32(i) >= 0 {