diff --git a/util/osutil/open_windows.go b/util/osutil/open_windows.go index 1116580..277f58b 100644 --- a/util/osutil/open_windows.go +++ b/util/osutil/open_windows.go @@ -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) } diff --git a/vfs/lock.go b/vfs/lock.go index 3815fd2..d8450d7 100644 --- a/vfs/lock.go +++ b/vfs/lock.go @@ -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 diff --git a/vfs/os_bsd.go b/vfs/os_bsd.go index 0fccfc5..441d3e9 100644 --- a/vfs/os_bsd.go +++ b/vfs/os_bsd.go @@ -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) { diff --git a/vfs/os_darwin.go b/vfs/os_darwin.go index 2077ceb..7c1ed95 100644 --- a/vfs/os_darwin.go +++ b/vfs/os_darwin.go @@ -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) } diff --git a/vfs/os_linux.go b/vfs/os_linux.go index a19d29d..8b89e88 100644 --- a/vfs/os_linux.go +++ b/vfs/os_linux.go @@ -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 { diff --git a/vfs/os_nolock.go b/vfs/os_nolock.go index 82379ec..7a38737 100644 --- a/vfs/os_nolock.go +++ b/vfs/os_nolock.go @@ -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 } diff --git a/vfs/os_ofd.go b/vfs/os_ofd.go index d3aa3ce..ced941e 100644 --- a/vfs/os_ofd.go +++ b/vfs/os_ofd.go @@ -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) } diff --git a/vfs/os_std_sleep.go b/vfs/os_std_sleep.go new file mode 100644 index 0000000..c6bc407 --- /dev/null +++ b/vfs/os_std_sleep.go @@ -0,0 +1,9 @@ +//go:build !windows || sqlite3_nosys + +package vfs + +import "time" + +func osSleep(d time.Duration) { + time.Sleep(d) +} diff --git a/vfs/os_std_sync.go b/vfs/os_std_sync.go index 6ea6aa1..84dbd23 100644 --- a/vfs/os_std_sync.go +++ b/vfs/os_std_sync.go @@ -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() } diff --git a/vfs/os_unix_lock.go b/vfs/os_unix_lock.go index 9cbe152..254bde8 100644 --- a/vfs/os_unix_lock.go +++ b/vfs/os_unix_lock.go @@ -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 { diff --git a/vfs/os_windows.go b/vfs/os_windows.go index 7ea9775..1c90c73 100644 --- a/vfs/os_windows.go +++ b/vfs/os_windows.go @@ -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) + } +} diff --git a/vfs/vfs.go b/vfs/vfs.go index d5efeab..1e8761b 100644 --- a/vfs/vfs.go +++ b/vfs/vfs.go @@ -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 }