Locking improvements.

This commit is contained in:
Nuno Cruces
2024-03-15 16:14:49 +00:00
parent 7bd31c3443
commit c61f7b90f6
12 changed files with 96 additions and 68 deletions

View File

@@ -97,6 +97,10 @@ func syscallOpen(path string, mode int, perm uint32) (fd Handle, err error) {
// Necessary for opening directory handles.
attrs |= FILE_FLAG_BACKUP_SEMANTICS
}
if mode&O_SYNC != 0 {
const _FILE_FLAG_WRITE_THROUGH = 0x80000000
attrs |= _FILE_FLAG_WRITE_THROUGH
}
return CreateFile(pathp, access, sharemode, sa, createmode, attrs, 0)
}

View File

@@ -67,7 +67,7 @@ func (f *vfsFile) Lock(lock LockLevel) error {
}
// A PENDING lock is needed before acquiring an EXCLUSIVE lock.
if f.lock < LOCK_PENDING {
if rc := osGetPendingLock(f.File); rc != _OK {
if rc := osGetPendingLock(f.File, f.lock); rc != _OK {
return rc
}
f.lock = LOCK_PENDING

View File

@@ -19,28 +19,17 @@ func osUnlock(file *os.File, start, len int64) _ErrorCode {
return _OK
}
func osLock(file *os.File, how int, timeout time.Duration, def _ErrorCode) _ErrorCode {
before := time.Now()
var err error
for {
err = unix.Flock(int(file.Fd()), how)
if errno, _ := err.(unix.Errno); errno != unix.EAGAIN {
break
}
if timeout <= 0 || timeout < time.Since(before) {
break
}
time.Sleep(time.Millisecond)
}
func osLock(file *os.File, how int, def _ErrorCode) _ErrorCode {
err := unix.Flock(int(file.Fd()), how)
return osLockErrorCode(err, def)
}
func osReadLock(file *os.File, start, len int64, timeout time.Duration) _ErrorCode {
return osLock(file, unix.LOCK_SH|unix.LOCK_NB, timeout, _IOERR_RDLOCK)
func osReadLock(file *os.File, _ /*start*/, _ /*len*/ int64, _ /*timeout*/ time.Duration) _ErrorCode {
return osLock(file, unix.LOCK_SH|unix.LOCK_NB, _IOERR_RDLOCK)
}
func osWriteLock(file *os.File, start, len int64, timeout time.Duration) _ErrorCode {
return osLock(file, unix.LOCK_EX|unix.LOCK_NB, timeout, _IOERR_LOCK)
func osWriteLock(file *os.File, _ /*start*/, _ /*len*/ int64, _ /*timeout*/ time.Duration) _ErrorCode {
return osLock(file, unix.LOCK_EX|unix.LOCK_NB, _IOERR_LOCK)
}
func osCheckLock(file *os.File, start, len int64) (bool, _ErrorCode) {

View File

@@ -23,7 +23,7 @@ type flocktimeout_t struct {
timeout unix.Timespec
}
func osSync(file *os.File, fullsync, dataonly bool) error {
func osSync(file *os.File, fullsync, _ /*dataonly*/ bool) error {
if fullsync {
return file.Sync()
}
@@ -75,9 +75,12 @@ func osLock(file *os.File, typ int16, start, len int64, timeout time.Duration, d
Len: len,
}}
var err error
if timeout == 0 {
switch {
case timeout == 0:
err = unix.FcntlFlock(file.Fd(), _F_OFD_SETLK, &lock.fl)
} else {
case timeout < 0:
err = unix.FcntlFlock(file.Fd(), _F_OFD_SETLKW, &lock.fl)
default:
lock.timeout = unix.NsecToTimespec(int64(timeout / time.Nanosecond))
err = unix.FcntlFlock(file.Fd(), _F_OFD_SETLKWTIMEOUT, &lock.fl)
}

View File

@@ -8,7 +8,7 @@ import (
"golang.org/x/sys/unix"
)
func osSync(file *os.File, fullsync, dataonly bool) error {
func osSync(file *os.File, _ /*fullsync*/, dataonly bool) error {
if dataonly {
_, _, err := unix.Syscall(unix.SYS_FDATASYNC, file.Fd(), 0, 0)
if err != 0 {

View File

@@ -12,30 +12,30 @@ import "os"
// [immutable]: https://sqlite.org/uri.html#uriimmutable
const SupportsFileLocking = false
func osGetSharedLock(file *os.File) _ErrorCode {
func osGetSharedLock(_ *os.File) _ErrorCode {
return _IOERR_RDLOCK
}
func osGetReservedLock(file *os.File) _ErrorCode {
func osGetReservedLock(_ *os.File) _ErrorCode {
return _IOERR_LOCK
}
func osGetPendingLock(file *os.File) _ErrorCode {
func osGetPendingLock(_ *os.File, _ LockLevel) _ErrorCode {
return _IOERR_LOCK
}
func osGetExclusiveLock(file *os.File) _ErrorCode {
func osGetExclusiveLock(_ *os.File) _ErrorCode {
return _IOERR_LOCK
}
func osDowngradeLock(file *os.File, state LockLevel) _ErrorCode {
func osDowngradeLock(_ *os.File, _ LockLevel) _ErrorCode {
return _IOERR_RDLOCK
}
func osReleaseLock(file *os.File, _ LockLevel) _ErrorCode {
func osReleaseLock(_ *os.File, _ LockLevel) _ErrorCode {
return _IOERR_UNLOCK
}
func osCheckReservedLock(file *os.File) (bool, _ErrorCode) {
func osCheckReservedLock(_ *os.File) (bool, _ErrorCode) {
return false, _IOERR_CHECKRESERVEDLOCK
}

View File

@@ -27,17 +27,24 @@ func osLock(file *os.File, typ int16, start, len int64, timeout time.Duration, d
Start: start,
Len: len,
}
before := time.Now()
var err error
for {
switch {
case timeout == 0:
err = unix.FcntlFlock(file.Fd(), unix.F_OFD_SETLK, &lock)
if errno, _ := err.(unix.Errno); errno != unix.EAGAIN {
break
case timeout < 0:
err = unix.FcntlFlock(file.Fd(), unix.F_OFD_SETLKW, &lock)
default:
before := time.Now()
for {
err = unix.FcntlFlock(file.Fd(), unix.F_OFD_SETLK, &lock)
if errno, _ := err.(unix.Errno); errno != unix.EAGAIN {
break
}
if timeout <= 0 || timeout < time.Since(before) {
break
}
osSleep(time.Millisecond)
}
if timeout <= 0 || timeout < time.Since(before) {
break
}
time.Sleep(time.Millisecond)
}
return osLockErrorCode(err, def)
}

9
vfs/os_std_sleep.go Normal file
View File

@@ -0,0 +1,9 @@
//go:build !windows || sqlite3_nosys
package vfs
import "time"
func osSleep(d time.Duration) {
time.Sleep(d)
}

View File

@@ -4,6 +4,6 @@ package vfs
import "os"
func osSync(file *os.File, fullsync, dataonly bool) error {
func osSync(file *os.File, _ /*fullsync*/, _ /*dataonly*/ bool) error {
return file.Sync()
}

View File

@@ -31,9 +31,13 @@ func osGetReservedLock(file *os.File) _ErrorCode {
return osWriteLock(file, _RESERVED_BYTE, 1, 0)
}
func osGetPendingLock(file *os.File) _ErrorCode {
func osGetPendingLock(file *os.File, state LockLevel) _ErrorCode {
// Acquire the PENDING lock.
return osWriteLock(file, _PENDING_BYTE, 1, 0)
var timeout time.Duration
if state >= LOCK_RESERVED {
timeout = -1
}
return osWriteLock(file, _PENDING_BYTE, 1, timeout)
}
func osGetExclusiveLock(file *os.File) _ErrorCode {

View File

@@ -36,9 +36,13 @@ func osGetReservedLock(file *os.File) _ErrorCode {
return osWriteLock(file, _RESERVED_BYTE, 1, 0)
}
func osGetPendingLock(file *os.File) _ErrorCode {
func osGetPendingLock(file *os.File, state LockLevel) _ErrorCode {
// Acquire the PENDING lock.
return osWriteLock(file, _PENDING_BYTE, 1, 0)
var timeout time.Duration
if state >= LOCK_RESERVED {
timeout = -1
}
return osWriteLock(file, _PENDING_BYTE, 1, timeout)
}
func osGetExclusiveLock(file *os.File) _ErrorCode {
@@ -110,44 +114,43 @@ func osUnlock(file *os.File, start, len uint32) _ErrorCode {
}
func osLock(file *os.File, flags, start, len uint32, timeout time.Duration, def _ErrorCode) _ErrorCode {
before := time.Now()
var err error
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 {
break
}
if timeout <= 0 || timeout < time.Since(before) {
break
}
if err := windows.TimeBeginPeriod(1); err != nil {
break
}
time.Sleep(time.Millisecond)
if err := windows.TimeEndPeriod(1); err != nil {
break
switch {
case timeout == 0:
err = osLockEx(file, flags|windows.LOCKFILE_FAIL_IMMEDIATELY, start, len)
case timeout < 0:
err = osLockEx(file, flags, start, len)
default:
before := time.Now()
for {
err = osLockEx(file, flags|windows.LOCKFILE_FAIL_IMMEDIATELY, start, len)
if errno, _ := err.(windows.Errno); errno != windows.ERROR_LOCK_VIOLATION {
break
}
if timeout <= 0 || timeout < time.Since(before) {
break
}
osSleep(time.Millisecond)
}
}
return osLockErrorCode(err, def)
}
func osLockEx(file *os.File, flags, start, len uint32) error {
return windows.LockFileEx(windows.Handle(file.Fd()), flags,
0, len, 0, &windows.Overlapped{Offset: start})
}
func osReadLock(file *os.File, start, len uint32, timeout time.Duration) _ErrorCode {
return osLock(file,
windows.LOCKFILE_FAIL_IMMEDIATELY,
start, len, timeout, _IOERR_RDLOCK)
return osLock(file, 0, start, len, timeout, _IOERR_RDLOCK)
}
func osWriteLock(file *os.File, start, len uint32, timeout time.Duration) _ErrorCode {
return osLock(file,
windows.LOCKFILE_FAIL_IMMEDIATELY|windows.LOCKFILE_EXCLUSIVE_LOCK,
start, len, timeout, _IOERR_LOCK)
return osLock(file, windows.LOCKFILE_EXCLUSIVE_LOCK, start, len, timeout, _IOERR_LOCK)
}
func osCheckLock(file *os.File, start, len uint32) (bool, _ErrorCode) {
rc := osLock(file,
windows.LOCKFILE_FAIL_IMMEDIATELY,
start, len, 0, _IOERR_CHECKRESERVEDLOCK)
rc := osLock(file, 0, start, len, 0, _IOERR_CHECKRESERVEDLOCK)
if rc == _BUSY {
return true, _OK
}
@@ -173,3 +176,12 @@ func osLockErrorCode(err error, def _ErrorCode) _ErrorCode {
}
return def
}
func osSleep(d time.Duration) {
if d > 0 {
period := uint32(max(1, min(d/(2*time.Millisecond), 16)))
windows.TimeBeginPeriod(period)
time.Sleep(d)
windows.TimeEndPeriod(period)
}
}

View File

@@ -79,7 +79,7 @@ func vfsRandomness(ctx context.Context, mod api.Module, pVfs, nByte, zByte uint3
}
func vfsSleep(ctx context.Context, mod api.Module, pVfs, nMicro uint32) _ErrorCode {
time.Sleep(time.Duration(nMicro) * time.Microsecond)
osSleep(time.Duration(nMicro) * time.Microsecond)
return _OK
}