Lock timeouts.

This commit is contained in:
Nuno Cruces
2023-03-18 01:13:31 +00:00
parent 15dec13f15
commit d0875e5fab
7 changed files with 77 additions and 37 deletions

View File

@@ -3,6 +3,7 @@ package sqlite3
import (
"context"
"os"
"time"
"github.com/tetratelabs/wazero/api"
)
@@ -61,3 +62,8 @@ func (vfsFileMethods) SetLock(ctx context.Context, mod api.Module, pFile uint32,
mem := memory{mod}
mem.writeUint8(pFile+vfsFileLockOffset, uint8(lock))
}
func (vfsFileMethods) GetLockTimeout(ctx context.Context, mod api.Module, pFile uint32) time.Duration {
mem := memory{mod}
return time.Duration(mem.readUint32(pFile+vfsFileLockTimeoutOffset)) * time.Millisecond
}

View File

@@ -3,6 +3,7 @@ package sqlite3
import (
"context"
"os"
"time"
"github.com/tetratelabs/wazero/api"
)
@@ -63,6 +64,7 @@ func vfsLock(ctx context.Context, mod api.Module, pFile uint32, eLock vfsLockSta
file := vfsFile.GetOS(ctx, mod, pFile)
cLock := vfsFile.GetLock(ctx, mod, pFile)
timeout := vfsFile.GetLockTimeout(ctx, mod, pFile)
switch {
case cLock < _NO_LOCK || cLock > _EXCLUSIVE_LOCK:
@@ -91,7 +93,7 @@ func vfsLock(ctx context.Context, mod api.Module, pFile uint32, eLock vfsLockSta
if locked, _ := vfsOS.CheckPendingLock(file); locked {
return uint32(BUSY)
}
if rc := vfsOS.GetSharedLock(file); rc != _OK {
if rc := vfsOS.GetSharedLock(file, timeout); rc != _OK {
return uint32(rc)
}
vfsFile.SetLock(ctx, mod, pFile, _SHARED_LOCK)
@@ -102,7 +104,7 @@ func vfsLock(ctx context.Context, mod api.Module, pFile uint32, eLock vfsLockSta
if cLock != _SHARED_LOCK {
panic(assertErr())
}
if rc := vfsOS.GetReservedLock(file); rc != _OK {
if rc := vfsOS.GetReservedLock(file, timeout); rc != _OK {
return uint32(rc)
}
vfsFile.SetLock(ctx, mod, pFile, _RESERVED_LOCK)
@@ -120,7 +122,7 @@ func vfsLock(ctx context.Context, mod api.Module, pFile uint32, eLock vfsLockSta
}
vfsFile.SetLock(ctx, mod, pFile, _PENDING_LOCK)
}
if rc := vfsOS.GetExclusiveLock(file); rc != _OK {
if rc := vfsOS.GetExclusiveLock(file, timeout); rc != _OK {
return uint32(rc)
}
vfsFile.SetLock(ctx, mod, pFile, _EXCLUSIVE_LOCK)
@@ -180,19 +182,19 @@ func vfsCheckReservedLock(ctx context.Context, mod api.Module, pFile, pResOut ui
return uint32(rc)
}
func (vfsOSMethods) GetSharedLock(file *os.File) xErrorCode {
func (vfsOSMethods) GetSharedLock(file *os.File, timeout time.Duration) xErrorCode {
// Acquire the SHARED lock.
return vfsOS.readLock(file, _SHARED_FIRST, _SHARED_SIZE)
return vfsOS.readLock(file, _SHARED_FIRST, _SHARED_SIZE, timeout)
}
func (vfsOSMethods) GetReservedLock(file *os.File) xErrorCode {
func (vfsOSMethods) GetReservedLock(file *os.File, timeout time.Duration) xErrorCode {
// Acquire the RESERVED lock.
return vfsOS.writeLock(file, _RESERVED_BYTE, 1)
return vfsOS.writeLock(file, _RESERVED_BYTE, 1, timeout)
}
func (vfsOSMethods) GetPendingLock(file *os.File) xErrorCode {
// Acquire the PENDING lock.
return vfsOS.writeLock(file, _PENDING_BYTE, 1)
return vfsOS.writeLock(file, _PENDING_BYTE, 1, 0)
}
func (vfsOSMethods) CheckReservedLock(file *os.File) (bool, xErrorCode) {

View File

@@ -43,7 +43,7 @@ func (vfsOSMethods) fcntlSetLock(file *os.File, lock unix.Flock_t) error {
return unix.FcntlFlock(file.Fd(), F_OFD_SETLK, &lock)
}
func (vfsOSMethods) fcntlSetLockTimeout(timeout time.Duration, file *os.File, lock unix.Flock_t) error {
func (vfsOSMethods) fcntlSetLockTimeout(file *os.File, lock unix.Flock_t, timeout time.Duration) error {
if timeout == 0 {
return vfsOS.fcntlSetLock(file, lock)
}

View File

@@ -34,6 +34,16 @@ func (vfsOSMethods) fcntlSetLock(file *os.File, lock unix.Flock_t) error {
return unix.FcntlFlock(file.Fd(), unix.F_OFD_SETLK, &lock)
}
func (vfsOSMethods) fcntlSetLockTimeout(timeout time.Duration, file *os.File, lock unix.Flock_t) error {
return vfsOS.fcntlSetLock(file, lock)
func (vfsOSMethods) fcntlSetLockTimeout(file *os.File, lock unix.Flock_t, timeout time.Duration) error {
for {
err := unix.FcntlFlock(file.Fd(), unix.F_OFD_SETLK, &lock)
if errno, _ := err.(unix.Errno); errno != unix.EAGAIN {
return err
}
if timeout < time.Millisecond {
return err
}
timeout -= time.Millisecond
time.Sleep(time.Millisecond)
}
}

View File

@@ -25,6 +25,6 @@ func (vfsOSMethods) fcntlSetLock(file *os.File, lock unix.Flock_t) error {
return notImplErr
}
func (vfsOSMethods) fcntlSetLockTimeout(timeout time.Duration, file *os.File, lock unix.Flock_t) error {
func (vfsOSMethods) fcntlSetLockTimeout(file *os.File, lock unix.Flock_t, timeout time.Duration) error {
return notImplErr
}

View File

@@ -5,6 +5,7 @@ package sqlite3
import (
"io/fs"
"os"
"time"
"golang.org/x/sys/unix"
)
@@ -24,15 +25,19 @@ func (vfsOSMethods) Access(path string, flags _AccessFlag) error {
return unix.Access(path, access)
}
func (vfsOSMethods) GetExclusiveLock(file *os.File) xErrorCode {
func (vfsOSMethods) GetExclusiveLock(file *os.File, timeout time.Duration) xErrorCode {
if timeout == 0 {
timeout = time.Millisecond
}
// Acquire the EXCLUSIVE lock.
return vfsOS.writeLock(file, _SHARED_FIRST, _SHARED_SIZE)
return vfsOS.writeLock(file, _SHARED_FIRST, _SHARED_SIZE, timeout)
}
func (vfsOSMethods) DowngradeLock(file *os.File, state vfsLockState) xErrorCode {
if state >= _EXCLUSIVE_LOCK {
// Downgrade to a SHARED lock.
if rc := vfsOS.readLock(file, _SHARED_FIRST, _SHARED_SIZE); rc != _OK {
if rc := vfsOS.readLock(file, _SHARED_FIRST, _SHARED_SIZE, 0); rc != _OK {
// In theory, the downgrade to a SHARED cannot fail because another
// process is holding an incompatible lock. If it does, this
// indicates that the other process is not following the locking
@@ -62,21 +67,21 @@ func (vfsOSMethods) unlock(file *os.File, start, len int64) xErrorCode {
return _OK
}
func (vfsOSMethods) readLock(file *os.File, start, len int64) xErrorCode {
return vfsOS.lockErrorCode(vfsOS.fcntlSetLock(file, unix.Flock_t{
func (vfsOSMethods) readLock(file *os.File, start, len int64, timeout time.Duration) xErrorCode {
return vfsOS.lockErrorCode(vfsOS.fcntlSetLockTimeout(file, unix.Flock_t{
Type: unix.F_RDLCK,
Start: start,
Len: len,
}), IOERR_RDLOCK)
}, timeout), IOERR_RDLOCK)
}
func (vfsOSMethods) writeLock(file *os.File, start, len int64) xErrorCode {
func (vfsOSMethods) writeLock(file *os.File, start, len int64, timeout time.Duration) xErrorCode {
// TODO: implement timeouts.
return vfsOS.lockErrorCode(vfsOS.fcntlSetLock(file, unix.Flock_t{
return vfsOS.lockErrorCode(vfsOS.fcntlSetLockTimeout(file, unix.Flock_t{
Type: unix.F_WRLCK,
Start: start,
Len: len,
}), IOERR_LOCK)
}, timeout), IOERR_LOCK)
}
func (vfsOSMethods) checkLock(file *os.File, start, len int64) (bool, xErrorCode) {

View File

@@ -5,6 +5,7 @@ import (
"io/fs"
"os"
"syscall"
"time"
"golang.org/x/sys/windows"
)
@@ -62,16 +63,20 @@ func (vfsOSMethods) Allocate(file *os.File, size int64) error {
return nil
}
func (vfsOSMethods) GetExclusiveLock(file *os.File) xErrorCode {
func (vfsOSMethods) GetExclusiveLock(file *os.File, timeout time.Duration) xErrorCode {
if timeout == 0 {
timeout = time.Millisecond
}
// Release the SHARED lock.
vfsOS.unlock(file, _SHARED_FIRST, _SHARED_SIZE)
// Acquire the EXCLUSIVE lock.
rc := vfsOS.writeLock(file, _SHARED_FIRST, _SHARED_SIZE)
rc := vfsOS.writeLock(file, _SHARED_FIRST, _SHARED_SIZE, timeout)
// Reacquire the SHARED lock.
if rc != _OK {
vfsOS.readLock(file, _SHARED_FIRST, _SHARED_SIZE)
vfsOS.readLock(file, _SHARED_FIRST, _SHARED_SIZE, 0)
}
return rc
}
@@ -82,7 +87,7 @@ func (vfsOSMethods) DowngradeLock(file *os.File, state vfsLockState) xErrorCode
vfsOS.unlock(file, _SHARED_FIRST, _SHARED_SIZE)
// Reacquire the SHARED lock.
if rc := vfsOS.readLock(file, _SHARED_FIRST, _SHARED_SIZE); rc != _OK {
if rc := vfsOS.readLock(file, _SHARED_FIRST, _SHARED_SIZE, 0); rc != _OK {
// This should never happen.
// We should always be able to reacquire the read lock.
return IOERR_RDLOCK
@@ -125,25 +130,37 @@ func (vfsOSMethods) unlock(file *os.File, start, len uint32) xErrorCode {
return _OK
}
func (vfsOSMethods) readLock(file *os.File, start, len uint32) xErrorCode {
return vfsOS.lockErrorCode(windows.LockFileEx(windows.Handle(file.Fd()),
windows.LOCKFILE_FAIL_IMMEDIATELY,
0, len, 0, &windows.Overlapped{Offset: start}),
IOERR_RDLOCK)
func (vfsOSMethods) lock(file *os.File, flags, start, len uint32, timeout time.Duration, def xErrorCode) xErrorCode {
for {
err := windows.LockFileEx(windows.Handle(file.Fd()), flags,
0, len, 0, &windows.Overlapped{Offset: start})
if errno, _ := err.(windows.Errno); errno != windows.ERROR_LOCK_VIOLATION {
return vfsOS.lockErrorCode(err, def)
}
if timeout < time.Millisecond {
return vfsOS.lockErrorCode(err, def)
}
timeout -= time.Millisecond
time.Sleep(time.Millisecond)
}
}
func (vfsOSMethods) writeLock(file *os.File, start, len uint32) xErrorCode {
return vfsOS.lockErrorCode(windows.LockFileEx(windows.Handle(file.Fd()),
func (vfsOSMethods) readLock(file *os.File, start, len uint32, timeout time.Duration) xErrorCode {
return vfsOS.lock(file,
windows.LOCKFILE_FAIL_IMMEDIATELY,
start, len, timeout, IOERR_RDLOCK)
}
func (vfsOSMethods) writeLock(file *os.File, start, len uint32, timeout time.Duration) xErrorCode {
return vfsOS.lock(file,
windows.LOCKFILE_FAIL_IMMEDIATELY|windows.LOCKFILE_EXCLUSIVE_LOCK,
0, len, 0, &windows.Overlapped{Offset: start}),
IOERR_LOCK)
start, len, timeout, IOERR_LOCK)
}
func (vfsOSMethods) checkLock(file *os.File, start, len uint32) (bool, xErrorCode) {
rc := vfsOS.lockErrorCode(windows.LockFileEx(windows.Handle(file.Fd()),
rc := vfsOS.lock(file,
windows.LOCKFILE_FAIL_IMMEDIATELY,
0, len, 0, &windows.Overlapped{Offset: start}),
IOERR_CHECKRESERVEDLOCK)
start, len, 0, IOERR_CHECKRESERVEDLOCK)
if rc == xErrorCode(BUSY) {
return true, _OK
}