From ea898fe317c1ba84cb8997414096f12289f30b92 Mon Sep 17 00:00:00 2001 From: Nuno Cruces Date: Fri, 17 Oct 2025 11:53:55 +0100 Subject: [PATCH 01/11] wip --- vfs/shm_bsd.go | 24 ++++++++++++------------ vfs/shm_dotlk.go | 14 +++++++------- vfs/shm_ofd.go | 24 ++++++++++++++---------- vfs/shm_windows.go | 20 ++++++++++++-------- 4 files changed, 45 insertions(+), 37 deletions(-) diff --git a/vfs/shm_bsd.go b/vfs/shm_bsd.go index 4545517f..1e518d9c 100644 --- a/vfs/shm_bsd.go +++ b/vfs/shm_bsd.go @@ -68,9 +68,9 @@ func (s *vfsShm) Close() error { panic(util.AssertErr()) } -func (s *vfsShm) shmOpen() (rc _ErrorCode) { +func (s *vfsShm) shmOpen() (err error) { if s.vfsShmParent != nil { - return _OK + return nil } vfsShmListMtx.Lock() @@ -80,7 +80,7 @@ func (s *vfsShm) shmOpen() (rc _ErrorCode) { // Closing it would release all POSIX locks on it. fi, err := os.Stat(s.path) if err != nil && !errors.Is(err, fs.ErrNotExist) { - return _IOERR_FSTAT + return sysError{err, _IOERR_FSTAT} } // Find a shared file, increase the reference count. @@ -88,7 +88,7 @@ func (s *vfsShm) shmOpen() (rc _ErrorCode) { if g != nil && os.SameFile(fi, g.info) { s.vfsShmParent = g g.refs++ - return _OK + return nil } } @@ -96,10 +96,10 @@ func (s *vfsShm) shmOpen() (rc _ErrorCode) { f, err := os.OpenFile(s.path, os.O_RDWR|os.O_CREATE|_O_NOFOLLOW, 0666) if err != nil { - return _CANTOPEN + return sysError{err, _CANTOPEN} } defer func() { - if rc != _OK { + if err != nil { f.Close() } }() @@ -114,7 +114,7 @@ func (s *vfsShm) shmOpen() (rc _ErrorCode) { return rc } if err := f.Truncate(0); err != nil { - return _IOERR_SHMOPEN + return sysError{err, _IOERR_SHMOPEN} } } if rc := osReadLock(f, _SHM_DMS, 1); rc != _OK { @@ -123,7 +123,7 @@ func (s *vfsShm) shmOpen() (rc _ErrorCode) { fi, err = f.Stat() if err != nil { - return _IOERR_FSTAT + return sysError{err, _IOERR_FSTAT} } // Add the new shared file. @@ -134,11 +134,11 @@ func (s *vfsShm) shmOpen() (rc _ErrorCode) { for i, g := range vfsShmList { if g == nil { vfsShmList[i] = s.vfsShmParent - return _OK + return nil } } vfsShmList = append(vfsShmList, s.vfsShmParent) - return _OK + return nil } func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, extend bool) (ptr_t, error) { @@ -147,8 +147,8 @@ func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, ext return 0, _IOERR_SHMMAP } - if rc := s.shmOpen(); rc != _OK { - return 0, rc + if err := s.shmOpen(); err != nil { + return 0, err } // Check if file is big enough. diff --git a/vfs/shm_dotlk.go b/vfs/shm_dotlk.go index 4dbfb3a5..8f2bc6ce 100644 --- a/vfs/shm_dotlk.go +++ b/vfs/shm_dotlk.go @@ -66,9 +66,9 @@ func (s *vfsShm) Close() error { return nil } -func (s *vfsShm) shmOpen() _ErrorCode { +func (s *vfsShm) shmOpen() error { if s.vfsShmParent != nil { - return _OK + return nil } vfsShmListMtx.Lock() @@ -78,7 +78,7 @@ func (s *vfsShm) shmOpen() _ErrorCode { if g, ok := vfsShmList[s.path]; ok { s.vfsShmParent = g g.refs++ - return _OK + return nil } // Dead man's switch. @@ -87,13 +87,13 @@ func (s *vfsShm) shmOpen() _ErrorCode { return _BUSY } if err != nil { - return _IOERR_LOCK + return sysError{err, _IOERR_LOCK} } // Add the new shared buffer. s.vfsShmParent = &vfsShmParent{} vfsShmList[s.path] = s.vfsShmParent - return _OK + return nil } func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, extend bool) (ptr_t, error) { @@ -105,8 +105,8 @@ func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, ext s.free = mod.ExportedFunction("sqlite3_free") s.alloc = mod.ExportedFunction("sqlite3_malloc64") } - if rc := s.shmOpen(); rc != _OK { - return 0, rc + if err := s.shmOpen(); err != nil { + return 0, err } s.Lock() diff --git a/vfs/shm_ofd.go b/vfs/shm_ofd.go index d7a37811..d7a85852 100644 --- a/vfs/shm_ofd.go +++ b/vfs/shm_ofd.go @@ -27,7 +27,10 @@ type vfsShm struct { var _ blockingSharedMemory = &vfsShm{} -func (s *vfsShm) shmOpen() _ErrorCode { +func (s *vfsShm) shmOpen() error { + if s.fileLock { + return nil + } if s.File == nil { f, err := os.OpenFile(s.path, os.O_RDWR|os.O_CREATE|_O_NOFOLLOW, 0666) @@ -37,13 +40,10 @@ func (s *vfsShm) shmOpen() _ErrorCode { s.readOnly = true } if err != nil { - return _CANTOPEN + return sysError{err, _CANTOPEN} } s.File = f } - if s.fileLock { - return _OK - } // Dead man's switch. if lock, rc := osTestLock(s.File, _SHM_DMS, 1); rc != _OK { @@ -65,12 +65,15 @@ func (s *vfsShm) shmOpen() _ErrorCode { return rc } if err := s.Truncate(0); err != nil { - return _IOERR_SHMOPEN + return sysError{err, _IOERR_SHMOPEN} } } rc := osReadLock(s.File, _SHM_DMS, 1, time.Millisecond) - s.fileLock = rc == _OK - return rc + if rc != _OK { + return rc + } + s.fileLock = true + return nil } func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, extend bool) (ptr_t, error) { @@ -79,8 +82,8 @@ func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, ext return 0, _IOERR_SHMMAP } - if rc := s.shmOpen(); rc != _OK { - return 0, rc + if err := s.shmOpen(); err != nil { + return 0, err } // Check if file is big enough. @@ -170,6 +173,7 @@ func (s *vfsShm) shmUnmap(delete bool) { } s.Close() s.File = nil + s.fileLock = false } func (s *vfsShm) shmBarrier() { diff --git a/vfs/shm_windows.go b/vfs/shm_windows.go index bf542066..b6f459ee 100644 --- a/vfs/shm_windows.go +++ b/vfs/shm_windows.go @@ -40,29 +40,32 @@ func (s *vfsShm) Close() error { return s.File.Close() } -func (s *vfsShm) shmOpen() _ErrorCode { +func (s *vfsShm) shmOpen() error { + if s.fileLock { + return nil + } if s.File == nil { f, err := os.OpenFile(s.path, os.O_RDWR|os.O_CREATE, 0666) if err != nil { - return _CANTOPEN + return sysError{err, _CANTOPEN} } s.File = f } - if s.fileLock { - return _OK - } // Dead man's switch. if rc := osWriteLock(s.File, _SHM_DMS, 1, 0); rc == _OK { err := s.Truncate(0) osUnlock(s.File, _SHM_DMS, 1) if err != nil { - return _IOERR_SHMOPEN + return sysError{err, _IOERR_SHMOPEN} } } rc := osReadLock(s.File, _SHM_DMS, 1, 0) - s.fileLock = rc == _OK - return rc + if rc != _OK { + return rc + } + s.fileLock = true + return nil } func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, extend bool) (_ ptr_t, err error) { @@ -172,6 +175,7 @@ func (s *vfsShm) shmUnmap(delete bool) { // Close the file. s.Close() s.File = nil + s.fileLock = false if delete { os.Remove(s.path) } From eec010bc53b8aa80cfc87a9b1135009ac84d0a5b Mon Sep 17 00:00:00 2001 From: Nuno Cruces Date: Fri, 17 Oct 2025 13:26:18 +0100 Subject: [PATCH 02/11] wip --- vfs/shm_ofd.go | 1 + vfs/shm_windows.go | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/vfs/shm_ofd.go b/vfs/shm_ofd.go index d7a85852..def03070 100644 --- a/vfs/shm_ofd.go +++ b/vfs/shm_ofd.go @@ -42,6 +42,7 @@ func (s *vfsShm) shmOpen() error { if err != nil { return sysError{err, _CANTOPEN} } + s.fileLock = false s.File = f } diff --git a/vfs/shm_windows.go b/vfs/shm_windows.go index b6f459ee..7c502489 100644 --- a/vfs/shm_windows.go +++ b/vfs/shm_windows.go @@ -49,6 +49,7 @@ func (s *vfsShm) shmOpen() error { if err != nil { return sysError{err, _CANTOPEN} } + s.fileLock = false s.File = f } @@ -78,8 +79,8 @@ func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, ext s.free = mod.ExportedFunction("sqlite3_free") s.alloc = mod.ExportedFunction("sqlite3_malloc64") } - if rc := s.shmOpen(); rc != _OK { - return 0, rc + if err := s.shmOpen(); err != nil { + return 0, err } defer s.shmAcquire(&err) From ca01edf9d93d90dace44874ea0e33609e9b0b012 Mon Sep 17 00:00:00 2001 From: Nuno Cruces Date: Fri, 17 Oct 2025 12:13:45 +0100 Subject: [PATCH 03/11] wip --- vfs/os_bsd.go | 6 +++--- vfs/os_dotlk.go | 7 ++----- vfs/os_ofd.go | 6 +++--- vfs/os_unix.go | 6 +++--- vfs/os_windows.go | 5 +++-- vfs/shm_bsd.go | 4 ++-- vfs/shm_ofd.go | 4 ++-- 7 files changed, 18 insertions(+), 20 deletions(-) diff --git a/vfs/os_bsd.go b/vfs/os_bsd.go index 4542f8e7..9077573e 100644 --- a/vfs/os_bsd.go +++ b/vfs/os_bsd.go @@ -61,13 +61,13 @@ func osReleaseLock(file *os.File, _ LockLevel) _ErrorCode { } } -func osCheckReservedLock(file *os.File) (bool, _ErrorCode) { +func osCheckReservedLock(file *os.File) (bool, error) { // Test the RESERVED lock with fcntl(F_GETLK). // This only works on systems where fcntl and flock are compatible. // However, SQLite only calls this while holding a shared lock, // so the difference is immaterial. - lock, rc := osTestLock(file, _RESERVED_BYTE, 1) - return lock == unix.F_WRLCK, rc + lock, err := osTestLock(file, _RESERVED_BYTE, 1) + return lock == unix.F_WRLCK, err } func osFlock(file *os.File, how int, def _ErrorCode) _ErrorCode { diff --git a/vfs/os_dotlk.go b/vfs/os_dotlk.go index 7a9c3889..f42bf7b2 100644 --- a/vfs/os_dotlk.go +++ b/vfs/os_dotlk.go @@ -130,14 +130,11 @@ func osReleaseLock(file *os.File, state LockLevel) _ErrorCode { return _OK } -func osCheckReservedLock(file *os.File) (bool, _ErrorCode) { +func osCheckReservedLock(file *os.File) (bool, error) { vfsDotLocksMtx.Lock() defer vfsDotLocksMtx.Unlock() name := file.Name() locker := vfsDotLocks[name] - if locker == nil { - return false, _OK - } - return locker.reserved != nil, _OK + return locker != nil && locker.reserved != nil, nil } diff --git a/vfs/os_ofd.go b/vfs/os_ofd.go index d93050e8..faa2edca 100644 --- a/vfs/os_ofd.go +++ b/vfs/os_ofd.go @@ -52,8 +52,8 @@ func osReleaseLock(file *os.File, _ LockLevel) _ErrorCode { return osUnlock(file, 0, 0) } -func osCheckReservedLock(file *os.File) (bool, _ErrorCode) { +func osCheckReservedLock(file *os.File) (bool, error) { // Test the RESERVED lock. - lock, rc := osTestLock(file, _RESERVED_BYTE, 1) - return lock == unix.F_WRLCK, rc + lock, err := osTestLock(file, _RESERVED_BYTE, 1) + return lock == unix.F_WRLCK, err } diff --git a/vfs/os_unix.go b/vfs/os_unix.go index 8aa07e47..38c9e31b 100644 --- a/vfs/os_unix.go +++ b/vfs/os_unix.go @@ -59,7 +59,7 @@ func osSetMode(file *os.File, modeof string) error { return nil } -func osTestLock(file *os.File, start, len int64) (int16, _ErrorCode) { +func osTestLock(file *os.File, start, len int64) (int16, error) { lock := unix.Flock_t{ Type: unix.F_WRLCK, Start: start, @@ -68,10 +68,10 @@ func osTestLock(file *os.File, start, len int64) (int16, _ErrorCode) { for { err := unix.FcntlFlock(file.Fd(), unix.F_GETLK, &lock) if err == nil { - return lock.Type, _OK + return lock.Type, nil } if err != unix.EINTR { - return 0, _IOERR_CHECKRESERVEDLOCK + return 0, sysError{err, _IOERR_CHECKRESERVEDLOCK} } } } diff --git a/vfs/os_windows.go b/vfs/os_windows.go index 19ad7a2e..503a4fcf 100644 --- a/vfs/os_windows.go +++ b/vfs/os_windows.go @@ -111,15 +111,16 @@ func osReleaseLock(file *os.File, state LockLevel) _ErrorCode { return _OK } -func osCheckReservedLock(file *os.File) (bool, _ErrorCode) { +func osCheckReservedLock(file *os.File) (bool, error) { // Test the RESERVED lock. rc := osLock(file, 0, _RESERVED_BYTE, 1, 0, _IOERR_CHECKRESERVEDLOCK) if rc == _BUSY { - return true, _OK + return true, nil } if rc == _OK { // Release the RESERVED lock. osUnlock(file, _RESERVED_BYTE, 1) + return false, nil } return false, rc } diff --git a/vfs/shm_bsd.go b/vfs/shm_bsd.go index 1e518d9c..b7c12945 100644 --- a/vfs/shm_bsd.go +++ b/vfs/shm_bsd.go @@ -105,8 +105,8 @@ func (s *vfsShm) shmOpen() (err error) { }() // Dead man's switch. - if lock, rc := osTestLock(f, _SHM_DMS, 1); rc != _OK { - return _IOERR_LOCK + if lock, err := osTestLock(f, _SHM_DMS, 1); err != nil { + return err } else if lock == unix.F_WRLCK { return _BUSY } else if lock == unix.F_UNLCK { diff --git a/vfs/shm_ofd.go b/vfs/shm_ofd.go index def03070..efd68a3d 100644 --- a/vfs/shm_ofd.go +++ b/vfs/shm_ofd.go @@ -47,8 +47,8 @@ func (s *vfsShm) shmOpen() error { } // Dead man's switch. - if lock, rc := osTestLock(s.File, _SHM_DMS, 1); rc != _OK { - return _IOERR_LOCK + if lock, err := osTestLock(s.File, _SHM_DMS, 1); err != _OK { + return err } else if lock == unix.F_WRLCK { return _BUSY } else if lock == unix.F_UNLCK { From 1f1cf713d68f5c86b7617d5719b63c01c516f54c Mon Sep 17 00:00:00 2001 From: Nuno Cruces Date: Fri, 17 Oct 2025 12:26:27 +0100 Subject: [PATCH 04/11] wip --- vfs/os_bsd.go | 6 +++--- vfs/os_darwin.go | 6 +++--- vfs/os_linux.go | 6 +++--- vfs/os_ofd.go | 4 ++-- vfs/os_windows.go | 8 ++++---- vfs/shm_bsd.go | 28 ++++++++++++++++------------ vfs/shm_memlk.go | 4 ++-- 7 files changed, 33 insertions(+), 29 deletions(-) diff --git a/vfs/os_bsd.go b/vfs/os_bsd.go index 9077573e..13bb3c74 100644 --- a/vfs/os_bsd.go +++ b/vfs/os_bsd.go @@ -92,7 +92,7 @@ func osLock(file *os.File, typ int16, start, len int64, def _ErrorCode) _ErrorCo return osLockErrorCode(err, def) } -func osUnlock(file *os.File, start, len int64) _ErrorCode { +func osUnlock(file *os.File, start, len int64) error { lock := unix.Flock_t{ Type: unix.F_UNLCK, Start: start, @@ -101,10 +101,10 @@ func osUnlock(file *os.File, start, len int64) _ErrorCode { for { err := unix.FcntlFlock(file.Fd(), unix.F_SETLK, &lock) if err == nil { - return _OK + return nil } if err != unix.EINTR { - return _IOERR_UNLOCK + return sysError{err, _IOERR_UNLOCK} } } } diff --git a/vfs/os_darwin.go b/vfs/os_darwin.go index 9bb8b559..9cb91dcc 100644 --- a/vfs/os_darwin.go +++ b/vfs/os_darwin.go @@ -103,7 +103,7 @@ func osLock(file *os.File, typ int16, start, len int64, timeout time.Duration, d return osLockErrorCode(err, def) } -func osUnlock(file *os.File, start, len int64) _ErrorCode { +func osUnlock(file *os.File, start, len int64) error { lock := unix.Flock_t{ Type: unix.F_UNLCK, Start: start, @@ -112,10 +112,10 @@ func osUnlock(file *os.File, start, len int64) _ErrorCode { for { err := unix.FcntlFlock(file.Fd(), _F_OFD_SETLK, &lock) if err == nil { - return _OK + return nil } if err != unix.EINTR { - return _IOERR_UNLOCK + return sysError{err, _IOERR_UNLOCK} } } } diff --git a/vfs/os_linux.go b/vfs/os_linux.go index 893f1512..cbcafe29 100644 --- a/vfs/os_linux.go +++ b/vfs/os_linux.go @@ -68,7 +68,7 @@ func osLock(file *os.File, typ int16, start, len int64, timeout time.Duration, d return osLockErrorCode(err, def) } -func osUnlock(file *os.File, start, len int64) _ErrorCode { +func osUnlock(file *os.File, start, len int64) error { lock := unix.Flock_t{ Type: unix.F_UNLCK, Start: start, @@ -77,10 +77,10 @@ func osUnlock(file *os.File, start, len int64) _ErrorCode { for { err := unix.FcntlFlock(file.Fd(), unix.F_OFD_SETLK, &lock) if err == nil { - return _OK + return nil } if err != unix.EINTR { - return _IOERR_UNLOCK + return sysError{err, _IOERR_UNLOCK} } } } diff --git a/vfs/os_ofd.go b/vfs/os_ofd.go index faa2edca..a7186975 100644 --- a/vfs/os_ofd.go +++ b/vfs/os_ofd.go @@ -35,7 +35,7 @@ func osGetExclusiveLock(file *os.File, state *LockLevel) _ErrorCode { return osWriteLock(file, _SHARED_FIRST, _SHARED_SIZE, time.Millisecond) } -func osDowngradeLock(file *os.File, state LockLevel) _ErrorCode { +func osDowngradeLock(file *os.File, state LockLevel) error { if state >= LOCK_EXCLUSIVE { // Downgrade to a SHARED lock. if rc := osReadLock(file, _SHARED_FIRST, _SHARED_SIZE, 0); rc != _OK { @@ -47,7 +47,7 @@ func osDowngradeLock(file *os.File, state LockLevel) _ErrorCode { return osUnlock(file, _PENDING_BYTE, 2) } -func osReleaseLock(file *os.File, _ LockLevel) _ErrorCode { +func osReleaseLock(file *os.File, _ LockLevel) error { // Release all locks. return osUnlock(file, 0, 0) } diff --git a/vfs/os_windows.go b/vfs/os_windows.go index 503a4fcf..aaca3d70 100644 --- a/vfs/os_windows.go +++ b/vfs/os_windows.go @@ -144,16 +144,16 @@ func osLock(file *os.File, flags, start, len uint32, timeout time.Duration, def return osLockErrorCode(err, def) } -func osUnlock(file *os.File, start, len uint32) _ErrorCode { +func osUnlock(file *os.File, start, len uint32) error { err := windows.UnlockFileEx(windows.Handle(file.Fd()), 0, len, 0, &windows.Overlapped{Offset: start}) if err == windows.ERROR_NOT_LOCKED { - return _OK + return nil } if err != nil { - return _IOERR_UNLOCK + return sysError{err, _IOERR_UNLOCK} } - return _OK + return nil } func osLockEx(file *os.File, flags, start, len uint32) error { diff --git a/vfs/shm_bsd.go b/vfs/shm_bsd.go index b7c12945..5846b62c 100644 --- a/vfs/shm_bsd.go +++ b/vfs/shm_bsd.go @@ -3,6 +3,7 @@ package vfs import ( + "cmp" "context" "errors" "io" @@ -182,9 +183,9 @@ func (s *vfsShm) shmLock(offset, n int32, flags _ShmFlag) error { defer s.Unlock() // Check if we can obtain/release locks locally. - rc := s.shmMemLock(offset, n, flags) - if rc != _OK { - return rc + err := s.shmMemLock(offset, n, flags) + if err != nil { + return err } // Obtain/release the appropriate file locks. @@ -196,36 +197,39 @@ func (s *vfsShm) shmLock(offset, n int32, flags _ShmFlag) error { for i := begin; i < end; i++ { if s.vfsShmParent.lock[i] != 0 { if i > begin { - rc |= osUnlock(s.File, _SHM_BASE+int64(begin), int64(i-begin)) + err = cmp.Or(err, + osUnlock(s.File, _SHM_BASE+int64(begin), int64(i-begin))) } begin = i + 1 } } if end > begin { - rc |= osUnlock(s.File, _SHM_BASE+int64(begin), int64(end-begin)) + err = cmp.Or(err, + osUnlock(s.File, _SHM_BASE+int64(begin), int64(end-begin))) } - return rc + return err case flags&_SHM_SHARED != 0: // Acquiring a new shared lock on the file is only necessary // if there was a new shared lock in the range. for i := offset; i < offset+n; i++ { if s.vfsShmParent.lock[i] == 1 { - rc = osReadLock(s.File, _SHM_BASE+int64(offset), int64(n)) + err = osReadLock(s.File, _SHM_BASE+int64(offset), int64(n)) break } } case flags&_SHM_EXCLUSIVE != 0: // Acquiring an exclusive lock on the file is always necessary. - rc = osWriteLock(s.File, _SHM_BASE+int64(offset), int64(n)) + err = osWriteLock(s.File, _SHM_BASE+int64(offset), int64(n)) default: panic(util.AssertErr()) } - // Release the local locks we had acquired. - if rc != _OK { - s.shmMemLock(offset, n, flags^(_SHM_UNLOCK|_SHM_LOCK)) + if err == nil || err == _OK { + return nil } - return rc + // Release the local locks we had acquired. + s.shmMemLock(offset, n, flags^(_SHM_UNLOCK|_SHM_LOCK)) + return err } func (s *vfsShm) shmUnmap(delete bool) { diff --git a/vfs/shm_memlk.go b/vfs/shm_memlk.go index 5c8071eb..ecfca2a1 100644 --- a/vfs/shm_memlk.go +++ b/vfs/shm_memlk.go @@ -5,7 +5,7 @@ package vfs import "github.com/ncruces/go-sqlite3/internal/util" // +checklocks:s.Mutex -func (s *vfsShm) shmMemLock(offset, n int32, flags _ShmFlag) _ErrorCode { +func (s *vfsShm) shmMemLock(offset, n int32, flags _ShmFlag) error { switch { case flags&_SHM_UNLOCK != 0: for i := offset; i < offset+n; i++ { @@ -49,5 +49,5 @@ func (s *vfsShm) shmMemLock(offset, n int32, flags _ShmFlag) _ErrorCode { panic(util.AssertErr()) } - return _OK + return nil } From 4722e55d1ba4e699c493b5ecec597b894a63323d Mon Sep 17 00:00:00 2001 From: Nuno Cruces Date: Fri, 17 Oct 2025 14:29:22 +0100 Subject: [PATCH 05/11] wip --- vfs/shm_copy.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vfs/shm_copy.go b/vfs/shm_copy.go index 4e20bcb5..2926cd4b 100644 --- a/vfs/shm_copy.go +++ b/vfs/shm_copy.go @@ -32,7 +32,7 @@ const ( // https://sqlite.org/walformat.html#the_wal_index_file_format func (s *vfsShm) shmAcquire(errp *error) { - if errp != nil && *errp != _OK { + if errp != nil && *errp != nil && *errp != _OK { return } if len(s.ptrs) == 0 || shmEqual(s.shadow[0][:], s.shared[0][:]) { From fcbbca5d6808bcb7ff02446adc96c7b7a26e410c Mon Sep 17 00:00:00 2001 From: Nuno Cruces Date: Fri, 17 Oct 2025 12:40:43 +0100 Subject: [PATCH 06/11] wip --- .github/workflows/libc.yml | 2 +- .github/workflows/test.yml | 2 +- vfs/file.go | 2 +- vfs/lock.go | 4 ++-- vfs/os_bsd.go | 6 +++--- vfs/os_dotlk.go | 6 +++--- vfs/os_windows.go | 4 ++-- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/libc.yml b/.github/workflows/libc.yml index b4317e17..f89a7053 100644 --- a/.github/workflows/libc.yml +++ b/.github/workflows/libc.yml @@ -10,7 +10,7 @@ jobs: test: strategy: matrix: - os: [ubuntu-24.04, ubuntu-24.04-arm, macos-13, macos-15] + os: [ubuntu-24.04, ubuntu-24.04-arm, macos-15, macos-15-intel] runs-on: ${{ matrix.os }} steps: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ebcb2e50..ef3caced 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -224,7 +224,7 @@ jobs: run: go test -v ./... test-macintel: - runs-on: macos-13 + runs-on: macos-15-intel needs: test steps: diff --git a/vfs/file.go b/vfs/file.go index 518f7dc7..1ae6cdd9 100644 --- a/vfs/file.go +++ b/vfs/file.go @@ -48,7 +48,7 @@ func (vfsOS) Delete(path string, syncDir bool) error { if isUnix && syncDir { f, err := os.Open(filepath.Dir(path)) if err != nil { - return _OK + return nil } defer f.Close() err = osSync(f, 0, SYNC_FULL) diff --git a/vfs/lock.go b/vfs/lock.go index 253057ae..8a36d75b 100644 --- a/vfs/lock.go +++ b/vfs/lock.go @@ -106,9 +106,9 @@ func (f *vfsFile) Unlock(lock LockLevel) error { return rc case LOCK_NONE: - rc := osReleaseLock(f.File, f.lock) + err := osReleaseLock(f.File, f.lock) f.lock = LOCK_NONE - return rc + return err default: panic(util.AssertErr()) diff --git a/vfs/os_bsd.go b/vfs/os_bsd.go index 13bb3c74..8a358633 100644 --- a/vfs/os_bsd.go +++ b/vfs/os_bsd.go @@ -49,14 +49,14 @@ func osDowngradeLock(file *os.File, _ LockLevel) _ErrorCode { return _OK } -func osReleaseLock(file *os.File, _ LockLevel) _ErrorCode { +func osReleaseLock(file *os.File, _ LockLevel) error { for { err := unix.Flock(int(file.Fd()), unix.LOCK_UN) if err == nil { - return _OK + return nil } if err != unix.EINTR { - return _IOERR_UNLOCK + return sysError{err, _IOERR_UNLOCK} } } } diff --git a/vfs/os_dotlk.go b/vfs/os_dotlk.go index f42bf7b2..23a2144f 100644 --- a/vfs/os_dotlk.go +++ b/vfs/os_dotlk.go @@ -103,7 +103,7 @@ func osDowngradeLock(file *os.File, _ LockLevel) _ErrorCode { return _OK } -func osReleaseLock(file *os.File, state LockLevel) _ErrorCode { +func osReleaseLock(file *os.File, state LockLevel) error { vfsDotLocksMtx.Lock() defer vfsDotLocksMtx.Unlock() @@ -115,7 +115,7 @@ func osReleaseLock(file *os.File, state LockLevel) _ErrorCode { if locker.shared == 1 { if err := dotlk.Unlock(name + ".lock"); err != nil { - return _IOERR_UNLOCK + return sysError{err, _IOERR_UNLOCK} } delete(vfsDotLocks, name) } @@ -127,7 +127,7 @@ func osReleaseLock(file *os.File, state LockLevel) _ErrorCode { locker.pending = nil } locker.shared-- - return _OK + return nil } func osCheckReservedLock(file *os.File) (bool, error) { diff --git a/vfs/os_windows.go b/vfs/os_windows.go index aaca3d70..d41ce16f 100644 --- a/vfs/os_windows.go +++ b/vfs/os_windows.go @@ -97,7 +97,7 @@ func osDowngradeLock(file *os.File, state LockLevel) _ErrorCode { return _OK } -func osReleaseLock(file *os.File, state LockLevel) _ErrorCode { +func osReleaseLock(file *os.File, state LockLevel) error { // Release all locks, PENDING must be last. if state >= LOCK_RESERVED { osUnlock(file, _RESERVED_BYTE, 1) @@ -108,7 +108,7 @@ func osReleaseLock(file *os.File, state LockLevel) _ErrorCode { if state >= LOCK_PENDING { osUnlock(file, _PENDING_BYTE, 1) } - return _OK + return nil } func osCheckReservedLock(file *os.File) (bool, error) { From 0c18d6111c5d490099270b9b919f18dae69ba0dd Mon Sep 17 00:00:00 2001 From: Nuno Cruces Date: Fri, 17 Oct 2025 12:48:47 +0100 Subject: [PATCH 07/11] wip --- vfs/lock.go | 4 ++-- vfs/os_bsd.go | 4 ++-- vfs/os_dotlk.go | 4 ++-- vfs/os_windows.go | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/vfs/lock.go b/vfs/lock.go index 8a36d75b..f729fb93 100644 --- a/vfs/lock.go +++ b/vfs/lock.go @@ -101,9 +101,9 @@ func (f *vfsFile) Unlock(lock LockLevel) error { switch lock { case LOCK_SHARED: - rc := osDowngradeLock(f.File, f.lock) + err := osDowngradeLock(f.File, f.lock) f.lock = LOCK_SHARED - return rc + return err case LOCK_NONE: err := osReleaseLock(f.File, f.lock) diff --git a/vfs/os_bsd.go b/vfs/os_bsd.go index 8a358633..23916120 100644 --- a/vfs/os_bsd.go +++ b/vfs/os_bsd.go @@ -36,7 +36,7 @@ func osGetExclusiveLock(file *os.File, state *LockLevel) _ErrorCode { return osGetReservedLock(file) } -func osDowngradeLock(file *os.File, _ LockLevel) _ErrorCode { +func osDowngradeLock(file *os.File, _ LockLevel) error { rc := osFlock(file, unix.LOCK_SH|unix.LOCK_NB, _IOERR_RDLOCK) if rc == _BUSY { // The documentation states that a lock is downgraded by @@ -46,7 +46,7 @@ func osDowngradeLock(file *os.File, _ LockLevel) _ErrorCode { // Return IOERR_RDLOCK, as BUSY would cause an assert to fail. return _IOERR_RDLOCK } - return _OK + return rc } func osReleaseLock(file *os.File, _ LockLevel) error { diff --git a/vfs/os_dotlk.go b/vfs/os_dotlk.go index 23a2144f..b8df2420 100644 --- a/vfs/os_dotlk.go +++ b/vfs/os_dotlk.go @@ -84,7 +84,7 @@ func osGetExclusiveLock(file *os.File, _ *LockLevel) _ErrorCode { return _OK } -func osDowngradeLock(file *os.File, _ LockLevel) _ErrorCode { +func osDowngradeLock(file *os.File, _ LockLevel) error { vfsDotLocksMtx.Lock() defer vfsDotLocksMtx.Unlock() @@ -100,7 +100,7 @@ func osDowngradeLock(file *os.File, _ LockLevel) _ErrorCode { if locker.pending == file { locker.pending = nil } - return _OK + return nil } func osReleaseLock(file *os.File, state LockLevel) error { diff --git a/vfs/os_windows.go b/vfs/os_windows.go index d41ce16f..5844e75b 100644 --- a/vfs/os_windows.go +++ b/vfs/os_windows.go @@ -75,7 +75,7 @@ func osGetExclusiveLock(file *os.File, state *LockLevel) _ErrorCode { return rc } -func osDowngradeLock(file *os.File, state LockLevel) _ErrorCode { +func osDowngradeLock(file *os.File, state LockLevel) error { if state >= LOCK_EXCLUSIVE { // Release the EXCLUSIVE lock while holding the PENDING lock. osUnlock(file, _SHARED_FIRST, _SHARED_SIZE) @@ -94,7 +94,7 @@ func osDowngradeLock(file *os.File, state LockLevel) _ErrorCode { if state >= LOCK_PENDING { osUnlock(file, _PENDING_BYTE, 1) } - return _OK + return nil } func osReleaseLock(file *os.File, state LockLevel) error { From 08fe7d9cec27b2f993996e3f0c692225674e3bbd Mon Sep 17 00:00:00 2001 From: Nuno Cruces Date: Fri, 17 Oct 2025 15:55:02 +0100 Subject: [PATCH 08/11] wip --- vfs/lock.go | 12 ++++++------ vfs/os_bsd.go | 28 +++++++++++++-------------- vfs/os_darwin.go | 6 +++--- vfs/os_dotlk.go | 14 +++++++------- vfs/os_linux.go | 6 +++--- vfs/os_ofd.go | 12 ++++++------ vfs/os_unix.go | 8 ++++---- vfs/os_windows.go | 48 +++++++++++++++++++++++----------------------- vfs/shm_bsd.go | 15 +++++++-------- vfs/shm_copy.go | 2 +- vfs/shm_memlk.go | 1 - vfs/shm_ofd.go | 15 ++++++--------- vfs/shm_windows.go | 11 ++++------- 13 files changed, 85 insertions(+), 93 deletions(-) diff --git a/vfs/lock.go b/vfs/lock.go index f729fb93..84c6e8da 100644 --- a/vfs/lock.go +++ b/vfs/lock.go @@ -51,8 +51,8 @@ func (f *vfsFile) Lock(lock LockLevel) error { if f.lock != LOCK_NONE { panic(util.AssertErr()) } - if rc := osGetSharedLock(f.File); rc != _OK { - return rc + if err := osGetSharedLock(f.File); err != nil { + return err } f.lock = LOCK_SHARED return nil @@ -62,8 +62,8 @@ func (f *vfsFile) Lock(lock LockLevel) error { if f.lock != LOCK_SHARED { panic(util.AssertErr()) } - if rc := osGetReservedLock(f.File); rc != _OK { - return rc + if err := osGetReservedLock(f.File); err != nil { + return err } f.lock = LOCK_RESERVED return nil @@ -73,8 +73,8 @@ func (f *vfsFile) Lock(lock LockLevel) error { if f.lock <= LOCK_NONE || f.lock >= LOCK_EXCLUSIVE { panic(util.AssertErr()) } - if rc := osGetExclusiveLock(f.File, &f.lock); rc != _OK { - return rc + if err := osGetExclusiveLock(f.File, &f.lock); err != nil { + return err } f.lock = LOCK_EXCLUSIVE return nil diff --git a/vfs/os_bsd.go b/vfs/os_bsd.go index 23916120..51a9d0cc 100644 --- a/vfs/os_bsd.go +++ b/vfs/os_bsd.go @@ -8,13 +8,13 @@ import ( "golang.org/x/sys/unix" ) -func osGetSharedLock(file *os.File) _ErrorCode { +func osGetSharedLock(file *os.File) error { return osFlock(file, unix.LOCK_SH|unix.LOCK_NB, _IOERR_RDLOCK) } -func osGetReservedLock(file *os.File) _ErrorCode { - rc := osFlock(file, unix.LOCK_EX|unix.LOCK_NB, _IOERR_LOCK) - if rc == _BUSY { +func osGetReservedLock(file *os.File) error { + err := osFlock(file, unix.LOCK_EX|unix.LOCK_NB, _IOERR_LOCK) + if err == _BUSY { // The documentation states that a lock is upgraded by // releasing the previous lock, then acquiring the new lock. // Going over the source code of various BSDs, though, @@ -26,19 +26,19 @@ func osGetReservedLock(file *os.File) _ErrorCode { // and invoke the busy handler if appropriate. return _BUSY_SNAPSHOT } - return rc + return err } -func osGetExclusiveLock(file *os.File, state *LockLevel) _ErrorCode { +func osGetExclusiveLock(file *os.File, state *LockLevel) error { if *state >= LOCK_RESERVED { - return _OK + return nil } return osGetReservedLock(file) } func osDowngradeLock(file *os.File, _ LockLevel) error { - rc := osFlock(file, unix.LOCK_SH|unix.LOCK_NB, _IOERR_RDLOCK) - if rc == _BUSY { + err := osFlock(file, unix.LOCK_SH|unix.LOCK_NB, _IOERR_RDLOCK) + if err == _BUSY { // The documentation states that a lock is downgraded by // releasing the previous lock then acquiring the new lock. // Going over the source code of various BSDs, though, @@ -46,7 +46,7 @@ func osDowngradeLock(file *os.File, _ LockLevel) error { // Return IOERR_RDLOCK, as BUSY would cause an assert to fail. return _IOERR_RDLOCK } - return rc + return err } func osReleaseLock(file *os.File, _ LockLevel) error { @@ -70,20 +70,20 @@ func osCheckReservedLock(file *os.File) (bool, error) { return lock == unix.F_WRLCK, err } -func osFlock(file *os.File, how int, def _ErrorCode) _ErrorCode { +func osFlock(file *os.File, how int, def _ErrorCode) error { err := unix.Flock(int(file.Fd()), how) return osLockErrorCode(err, def) } -func osReadLock(file *os.File, start, len int64) _ErrorCode { +func osReadLock(file *os.File, start, len int64) error { return osLock(file, unix.F_RDLCK, start, len, _IOERR_RDLOCK) } -func osWriteLock(file *os.File, start, len int64) _ErrorCode { +func osWriteLock(file *os.File, start, len int64) error { return osLock(file, unix.F_WRLCK, start, len, _IOERR_LOCK) } -func osLock(file *os.File, typ int16, start, len int64, def _ErrorCode) _ErrorCode { +func osLock(file *os.File, typ int16, start, len int64, def _ErrorCode) error { err := unix.FcntlFlock(file.Fd(), unix.F_SETLK, &unix.Flock_t{ Type: typ, Start: start, diff --git a/vfs/os_darwin.go b/vfs/os_darwin.go index 9cb91dcc..6748c299 100644 --- a/vfs/os_darwin.go +++ b/vfs/os_darwin.go @@ -75,15 +75,15 @@ func osAllocate(file *os.File, size int64) error { return file.Truncate(size) } -func osReadLock(file *os.File, start, len int64, timeout time.Duration) _ErrorCode { +func osReadLock(file *os.File, start, len int64, timeout time.Duration) error { return osLock(file, unix.F_RDLCK, start, len, timeout, _IOERR_RDLOCK) } -func osWriteLock(file *os.File, start, len int64, timeout time.Duration) _ErrorCode { +func osWriteLock(file *os.File, start, len int64, timeout time.Duration) error { return osLock(file, unix.F_WRLCK, start, len, timeout, _IOERR_LOCK) } -func osLock(file *os.File, typ int16, start, len int64, timeout time.Duration, def _ErrorCode) _ErrorCode { +func osLock(file *os.File, typ int16, start, len int64, timeout time.Duration, def _ErrorCode) error { lock := &flocktimeout_t{fl: unix.Flock_t{ Type: typ, Start: start, diff --git a/vfs/os_dotlk.go b/vfs/os_dotlk.go index b8df2420..b64e8ec0 100644 --- a/vfs/os_dotlk.go +++ b/vfs/os_dotlk.go @@ -23,7 +23,7 @@ type vfsDotLocker struct { reserved *os.File // +checklocks:vfsDotLocksMtx } -func osGetSharedLock(file *os.File) _ErrorCode { +func osGetSharedLock(file *os.File) error { vfsDotLocksMtx.Lock() defer vfsDotLocksMtx.Unlock() @@ -34,7 +34,7 @@ func osGetSharedLock(file *os.File) _ErrorCode { if errors.Is(err, fs.ErrExist) { return _BUSY // Another process has the lock. } - return _IOERR_LOCK + return sysError{err, _IOERR_LOCK} } locker = &vfsDotLocker{} vfsDotLocks[name] = locker @@ -44,10 +44,10 @@ func osGetSharedLock(file *os.File) _ErrorCode { return _BUSY } locker.shared++ - return _OK + return nil } -func osGetReservedLock(file *os.File) _ErrorCode { +func osGetReservedLock(file *os.File) error { vfsDotLocksMtx.Lock() defer vfsDotLocksMtx.Unlock() @@ -61,10 +61,10 @@ func osGetReservedLock(file *os.File) _ErrorCode { return _BUSY } locker.reserved = file - return _OK + return nil } -func osGetExclusiveLock(file *os.File, _ *LockLevel) _ErrorCode { +func osGetExclusiveLock(file *os.File, _ *LockLevel) error { vfsDotLocksMtx.Lock() defer vfsDotLocksMtx.Unlock() @@ -81,7 +81,7 @@ func osGetExclusiveLock(file *os.File, _ *LockLevel) _ErrorCode { if locker.shared > 1 { return _BUSY } - return _OK + return nil } func osDowngradeLock(file *os.File, _ LockLevel) error { diff --git a/vfs/os_linux.go b/vfs/os_linux.go index cbcafe29..1f7705ca 100644 --- a/vfs/os_linux.go +++ b/vfs/os_linux.go @@ -44,15 +44,15 @@ func osAllocate(file *os.File, size int64) error { } -func osReadLock(file *os.File, start, len int64, timeout time.Duration) _ErrorCode { +func osReadLock(file *os.File, start, len int64, timeout time.Duration) error { return osLock(file, unix.F_RDLCK, start, len, timeout, _IOERR_RDLOCK) } -func osWriteLock(file *os.File, start, len int64, timeout time.Duration) _ErrorCode { +func osWriteLock(file *os.File, start, len int64, timeout time.Duration) error { return osLock(file, unix.F_WRLCK, start, len, timeout, _IOERR_LOCK) } -func osLock(file *os.File, typ int16, start, len int64, timeout time.Duration, def _ErrorCode) _ErrorCode { +func osLock(file *os.File, typ int16, start, len int64, timeout time.Duration, def _ErrorCode) error { lock := unix.Flock_t{ Type: typ, Start: start, diff --git a/vfs/os_ofd.go b/vfs/os_ofd.go index a7186975..1fc365e0 100644 --- a/vfs/os_ofd.go +++ b/vfs/os_ofd.go @@ -9,7 +9,7 @@ import ( "golang.org/x/sys/unix" ) -func osGetSharedLock(file *os.File) _ErrorCode { +func osGetSharedLock(file *os.File) error { // Test the PENDING lock before acquiring a new SHARED lock. if lock, _ := osTestLock(file, _PENDING_BYTE, 1); lock == unix.F_WRLCK { return _BUSY @@ -18,16 +18,16 @@ func osGetSharedLock(file *os.File) _ErrorCode { return osReadLock(file, _SHARED_FIRST, _SHARED_SIZE, 0) } -func osGetReservedLock(file *os.File) _ErrorCode { +func osGetReservedLock(file *os.File) error { // Acquire the RESERVED lock. return osWriteLock(file, _RESERVED_BYTE, 1, 0) } -func osGetExclusiveLock(file *os.File, state *LockLevel) _ErrorCode { +func osGetExclusiveLock(file *os.File, state *LockLevel) error { if *state == LOCK_RESERVED { // A PENDING lock is needed before acquiring an EXCLUSIVE lock. - if rc := osWriteLock(file, _PENDING_BYTE, 1, -1); rc != _OK { - return rc + if err := osWriteLock(file, _PENDING_BYTE, 1, -1); err != nil { + return err } *state = LOCK_PENDING } @@ -38,7 +38,7 @@ func osGetExclusiveLock(file *os.File, state *LockLevel) _ErrorCode { func osDowngradeLock(file *os.File, state LockLevel) error { if state >= LOCK_EXCLUSIVE { // Downgrade to a SHARED lock. - if rc := osReadLock(file, _SHARED_FIRST, _SHARED_SIZE, 0); rc != _OK { + if err := osReadLock(file, _SHARED_FIRST, _SHARED_SIZE, 0); err != nil { // notest // this should never happen return _IOERR_RDLOCK } diff --git a/vfs/os_unix.go b/vfs/os_unix.go index 38c9e31b..1b85efda 100644 --- a/vfs/os_unix.go +++ b/vfs/os_unix.go @@ -76,9 +76,9 @@ func osTestLock(file *os.File, start, len int64) (int16, error) { } } -func osLockErrorCode(err error, def _ErrorCode) _ErrorCode { +func osLockErrorCode(err error, def _ErrorCode) error { if err == nil { - return _OK + return nil } if errno, ok := err.(unix.Errno); ok { switch errno { @@ -92,12 +92,12 @@ func osLockErrorCode(err error, def _ErrorCode) _ErrorCode { unix.ETIMEDOUT: return _BUSY case unix.EPERM: - return _PERM + return sysError{err, _PERM} } // notest // usually EWOULDBLOCK == EAGAIN if errno == unix.EWOULDBLOCK && unix.EWOULDBLOCK != unix.EAGAIN { return _BUSY } } - return def + return sysError{err, def} } diff --git a/vfs/os_windows.go b/vfs/os_windows.go index 5844e75b..eb843e39 100644 --- a/vfs/os_windows.go +++ b/vfs/os_windows.go @@ -26,25 +26,25 @@ func osWriteAt(file *os.File, p []byte, off int64) (int, error) { return n, err } -func osGetSharedLock(file *os.File) _ErrorCode { +func osGetSharedLock(file *os.File) error { // Acquire the PENDING lock temporarily before acquiring a new SHARED lock. - rc := osReadLock(file, _PENDING_BYTE, 1, 0) - if rc == _OK { + err := osReadLock(file, _PENDING_BYTE, 1, 0) + if err == nil { // Acquire the SHARED lock. - rc = osReadLock(file, _SHARED_FIRST, _SHARED_SIZE, 0) + err = osReadLock(file, _SHARED_FIRST, _SHARED_SIZE, 0) // Release the PENDING lock. osUnlock(file, _PENDING_BYTE, 1) } - return rc + return err } -func osGetReservedLock(file *os.File) _ErrorCode { +func osGetReservedLock(file *os.File) error { // Acquire the RESERVED lock. return osWriteLock(file, _RESERVED_BYTE, 1, 0) } -func osGetExclusiveLock(file *os.File, state *LockLevel) _ErrorCode { +func osGetExclusiveLock(file *os.File, state *LockLevel) error { // A PENDING lock is needed before releasing the SHARED lock. if *state < LOCK_PENDING { // If we were RESERVED, we can block indefinitely. @@ -52,8 +52,8 @@ func osGetExclusiveLock(file *os.File, state *LockLevel) _ErrorCode { if *state == LOCK_RESERVED { timeout = -1 } - if rc := osWriteLock(file, _PENDING_BYTE, 1, timeout); rc != _OK { - return rc + if err := osWriteLock(file, _PENDING_BYTE, 1, timeout); err != nil { + return err } *state = LOCK_PENDING } @@ -63,16 +63,16 @@ func osGetExclusiveLock(file *os.File, state *LockLevel) _ErrorCode { // Acquire the EXCLUSIVE lock. // Can't wait here, because the file is not OVERLAPPED. - rc := osWriteLock(file, _SHARED_FIRST, _SHARED_SIZE, 0) + err := osWriteLock(file, _SHARED_FIRST, _SHARED_SIZE, 0) - if rc != _OK { + if err != nil { // Reacquire the SHARED lock. - if rc := osReadLock(file, _SHARED_FIRST, _SHARED_SIZE, 0); rc != _OK { + if err := osReadLock(file, _SHARED_FIRST, _SHARED_SIZE, 0); err != nil { // notest // this should never happen return _IOERR_RDLOCK } } - return rc + return err } func osDowngradeLock(file *os.File, state LockLevel) error { @@ -81,7 +81,7 @@ func osDowngradeLock(file *os.File, state LockLevel) error { osUnlock(file, _SHARED_FIRST, _SHARED_SIZE) // Reacquire the SHARED lock. - if rc := osReadLock(file, _SHARED_FIRST, _SHARED_SIZE, 0); rc != _OK { + if err := osReadLock(file, _SHARED_FIRST, _SHARED_SIZE, 0); err != nil { // notest // this should never happen return _IOERR_RDLOCK } @@ -113,27 +113,27 @@ func osReleaseLock(file *os.File, state LockLevel) error { func osCheckReservedLock(file *os.File) (bool, error) { // Test the RESERVED lock. - rc := osLock(file, 0, _RESERVED_BYTE, 1, 0, _IOERR_CHECKRESERVEDLOCK) - if rc == _BUSY { + err := osLock(file, 0, _RESERVED_BYTE, 1, 0, _IOERR_CHECKRESERVEDLOCK) + if err == _BUSY { return true, nil } - if rc == _OK { + if err == nil { // Release the RESERVED lock. osUnlock(file, _RESERVED_BYTE, 1) return false, nil } - return false, rc + return false, err } -func osReadLock(file *os.File, start, len uint32, timeout time.Duration) _ErrorCode { +func osReadLock(file *os.File, start, len uint32, timeout time.Duration) error { return osLock(file, 0, start, len, timeout, _IOERR_RDLOCK) } -func osWriteLock(file *os.File, start, len uint32, timeout time.Duration) _ErrorCode { +func osWriteLock(file *os.File, start, len uint32, timeout time.Duration) error { return osLock(file, windows.LOCKFILE_EXCLUSIVE_LOCK, start, len, timeout, _IOERR_LOCK) } -func osLock(file *os.File, flags, start, len uint32, timeout time.Duration, def _ErrorCode) _ErrorCode { +func osLock(file *os.File, flags, start, len uint32, timeout time.Duration, def _ErrorCode) error { var err error switch { default: @@ -161,9 +161,9 @@ func osLockEx(file *os.File, flags, start, len uint32) error { 0, len, 0, &windows.Overlapped{Offset: start}) } -func osLockErrorCode(err error, def _ErrorCode) _ErrorCode { +func osLockErrorCode(err error, def _ErrorCode) error { if err == nil { - return _OK + return nil } if errno, ok := err.(windows.Errno); ok { // https://devblogs.microsoft.com/oldnewthing/20140905-00/?p=63 @@ -176,5 +176,5 @@ func osLockErrorCode(err error, def _ErrorCode) _ErrorCode { return _BUSY } } - return def + return sysError{err, def} } diff --git a/vfs/shm_bsd.go b/vfs/shm_bsd.go index 5846b62c..3aa61dac 100644 --- a/vfs/shm_bsd.go +++ b/vfs/shm_bsd.go @@ -111,15 +111,15 @@ func (s *vfsShm) shmOpen() (err error) { } else if lock == unix.F_WRLCK { return _BUSY } else if lock == unix.F_UNLCK { - if rc := osWriteLock(f, _SHM_DMS, 1); rc != _OK { - return rc + if err := osWriteLock(f, _SHM_DMS, 1); err != nil { + return err } if err := f.Truncate(0); err != nil { return sysError{err, _IOERR_SHMOPEN} } } - if rc := osReadLock(f, _SHM_DMS, 1); rc != _OK { - return rc + if err := osReadLock(f, _SHM_DMS, 1); err != nil { + return err } fi, err = f.Stat() @@ -224,11 +224,10 @@ func (s *vfsShm) shmLock(offset, n int32, flags _ShmFlag) error { panic(util.AssertErr()) } - if err == nil || err == _OK { - return nil + if err != nil { + // Release the local locks we had acquired. + s.shmMemLock(offset, n, flags^(_SHM_UNLOCK|_SHM_LOCK)) } - // Release the local locks we had acquired. - s.shmMemLock(offset, n, flags^(_SHM_UNLOCK|_SHM_LOCK)) return err } diff --git a/vfs/shm_copy.go b/vfs/shm_copy.go index 2926cd4b..8e40aafb 100644 --- a/vfs/shm_copy.go +++ b/vfs/shm_copy.go @@ -32,7 +32,7 @@ const ( // https://sqlite.org/walformat.html#the_wal_index_file_format func (s *vfsShm) shmAcquire(errp *error) { - if errp != nil && *errp != nil && *errp != _OK { + if errp != nil && *errp != nil { return } if len(s.ptrs) == 0 || shmEqual(s.shadow[0][:], s.shared[0][:]) { diff --git a/vfs/shm_memlk.go b/vfs/shm_memlk.go index ecfca2a1..d23c26c3 100644 --- a/vfs/shm_memlk.go +++ b/vfs/shm_memlk.go @@ -48,6 +48,5 @@ func (s *vfsShm) shmMemLock(offset, n int32, flags _ShmFlag) error { default: panic(util.AssertErr()) } - return nil } diff --git a/vfs/shm_ofd.go b/vfs/shm_ofd.go index efd68a3d..0ba2a7ac 100644 --- a/vfs/shm_ofd.go +++ b/vfs/shm_ofd.go @@ -47,7 +47,7 @@ func (s *vfsShm) shmOpen() error { } // Dead man's switch. - if lock, err := osTestLock(s.File, _SHM_DMS, 1); err != _OK { + if lock, err := osTestLock(s.File, _SHM_DMS, 1); err != nil { return err } else if lock == unix.F_WRLCK { return _BUSY @@ -62,19 +62,16 @@ func (s *vfsShm) shmOpen() error { // 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 + if err := osWriteLock(s.File, _SHM_DMS, 1, 0); err != nil { + return err } if err := s.Truncate(0); err != nil { return sysError{err, _IOERR_SHMOPEN} } } - rc := osReadLock(s.File, _SHM_DMS, 1, time.Millisecond) - if rc != _OK { - return rc - } - s.fileLock = true - return nil + err := osReadLock(s.File, _SHM_DMS, 1, time.Millisecond) + s.fileLock = err == nil + return err } func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, extend bool) (ptr_t, error) { diff --git a/vfs/shm_windows.go b/vfs/shm_windows.go index 7c502489..ad3e153a 100644 --- a/vfs/shm_windows.go +++ b/vfs/shm_windows.go @@ -54,19 +54,16 @@ func (s *vfsShm) shmOpen() error { } // Dead man's switch. - if rc := osWriteLock(s.File, _SHM_DMS, 1, 0); rc == _OK { + if osWriteLock(s.File, _SHM_DMS, 1, 0) == nil { err := s.Truncate(0) osUnlock(s.File, _SHM_DMS, 1) if err != nil { return sysError{err, _IOERR_SHMOPEN} } } - rc := osReadLock(s.File, _SHM_DMS, 1, 0) - if rc != _OK { - return rc - } - s.fileLock = true - return nil + err := osReadLock(s.File, _SHM_DMS, 1, 0) + s.fileLock = err == nil + return err } func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, extend bool) (_ ptr_t, err error) { From 5e9764067cd0888c940270dac16c730b19684841 Mon Sep 17 00:00:00 2001 From: Nuno Cruces Date: Fri, 17 Oct 2025 16:06:55 +0100 Subject: [PATCH 09/11] wip --- vfs/const.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vfs/const.go b/vfs/const.go index 87e19d95..ffb84dd7 100644 --- a/vfs/const.go +++ b/vfs/const.go @@ -24,7 +24,7 @@ func (e _ErrorCode) Error() string { } const ( - _OK _ErrorCode = util.OK + _OK = util.OK _ERROR _ErrorCode = util.ERROR _PERM _ErrorCode = util.PERM _BUSY _ErrorCode = util.BUSY From b3b74c0d6d7c21fc16609a3a770975c4929445a9 Mon Sep 17 00:00:00 2001 From: Nuno Cruces Date: Fri, 17 Oct 2025 16:20:57 +0100 Subject: [PATCH 10/11] wip --- vfs/os_bsd.go | 2 +- vfs/os_ofd.go | 4 ++-- vfs/os_unix.go | 4 ++-- vfs/shm_bsd.go | 2 +- vfs/shm_ofd.go | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/vfs/os_bsd.go b/vfs/os_bsd.go index 51a9d0cc..a70579d2 100644 --- a/vfs/os_bsd.go +++ b/vfs/os_bsd.go @@ -66,7 +66,7 @@ func osCheckReservedLock(file *os.File) (bool, error) { // This only works on systems where fcntl and flock are compatible. // However, SQLite only calls this while holding a shared lock, // so the difference is immaterial. - lock, err := osTestLock(file, _RESERVED_BYTE, 1) + lock, err := osTestLock(file, _RESERVED_BYTE, 1, _IOERR_CHECKRESERVEDLOCK) return lock == unix.F_WRLCK, err } diff --git a/vfs/os_ofd.go b/vfs/os_ofd.go index 1fc365e0..e917e12d 100644 --- a/vfs/os_ofd.go +++ b/vfs/os_ofd.go @@ -11,7 +11,7 @@ import ( func osGetSharedLock(file *os.File) error { // Test the PENDING lock before acquiring a new SHARED lock. - if lock, _ := osTestLock(file, _PENDING_BYTE, 1); lock == unix.F_WRLCK { + if lock, _ := osTestLock(file, _PENDING_BYTE, 1, _IOERR); lock == unix.F_WRLCK { return _BUSY } // Acquire the SHARED lock. @@ -54,6 +54,6 @@ func osReleaseLock(file *os.File, _ LockLevel) error { func osCheckReservedLock(file *os.File) (bool, error) { // Test the RESERVED lock. - lock, err := osTestLock(file, _RESERVED_BYTE, 1) + lock, err := osTestLock(file, _RESERVED_BYTE, 1, _IOERR_CHECKRESERVEDLOCK) return lock == unix.F_WRLCK, err } diff --git a/vfs/os_unix.go b/vfs/os_unix.go index 1b85efda..69fdc8fb 100644 --- a/vfs/os_unix.go +++ b/vfs/os_unix.go @@ -59,7 +59,7 @@ func osSetMode(file *os.File, modeof string) error { return nil } -func osTestLock(file *os.File, start, len int64) (int16, error) { +func osTestLock(file *os.File, start, len int64, def _ErrorCode) (int16, error) { lock := unix.Flock_t{ Type: unix.F_WRLCK, Start: start, @@ -71,7 +71,7 @@ func osTestLock(file *os.File, start, len int64) (int16, error) { return lock.Type, nil } if err != unix.EINTR { - return 0, sysError{err, _IOERR_CHECKRESERVEDLOCK} + return 0, sysError{err, def} } } } diff --git a/vfs/shm_bsd.go b/vfs/shm_bsd.go index 3aa61dac..ebf8418c 100644 --- a/vfs/shm_bsd.go +++ b/vfs/shm_bsd.go @@ -106,7 +106,7 @@ func (s *vfsShm) shmOpen() (err error) { }() // Dead man's switch. - if lock, err := osTestLock(f, _SHM_DMS, 1); err != nil { + if lock, err := osTestLock(f, _SHM_DMS, 1, _IOERR_LOCK); err != nil { return err } else if lock == unix.F_WRLCK { return _BUSY diff --git a/vfs/shm_ofd.go b/vfs/shm_ofd.go index 0ba2a7ac..de31e59d 100644 --- a/vfs/shm_ofd.go +++ b/vfs/shm_ofd.go @@ -47,7 +47,7 @@ func (s *vfsShm) shmOpen() error { } // Dead man's switch. - if lock, err := osTestLock(s.File, _SHM_DMS, 1); err != nil { + if lock, err := osTestLock(s.File, _SHM_DMS, 1, _IOERR_LOCK); err != nil { return err } else if lock == unix.F_WRLCK { return _BUSY From a7d6ba47fa04d07721608ec6806de56279a586b1 Mon Sep 17 00:00:00 2001 From: Nuno Cruces Date: Fri, 17 Oct 2025 16:31:22 +0100 Subject: [PATCH 11/11] wip --- error.go | 4 ++++ sqlite.go | 2 +- vfs/api.go | 8 +++++--- vfs/vfs.go | 2 ++ 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/error.go b/error.go index 3dfea7b5..8d0419c5 100644 --- a/error.go +++ b/error.go @@ -40,6 +40,10 @@ func (e *Error) Error() string { b.WriteString(": ") b.WriteString(e.msg) } + if e.sys != nil { + b.WriteString(": ") + b.WriteString(e.sys.Error()) + } return b.String() } diff --git a/sqlite.go b/sqlite.go index 67f961cb..52532e07 100644 --- a/sqlite.go +++ b/sqlite.go @@ -132,7 +132,7 @@ func (sqlt *sqlite) error(rc res_t, handle ptr_t, sql ...string) error { msg = strings.TrimPrefix(msg, "sqlite3: ") msg = strings.TrimPrefix(msg, util.ErrorCodeString(rc)[len("sqlite3: "):]) msg = strings.TrimPrefix(msg, ": ") - if msg == "not an error" { + if msg == "" || msg == "not an error" { msg = "" } } diff --git a/vfs/api.go b/vfs/api.go index 46a716e6..ef0623e5 100644 --- a/vfs/api.go +++ b/vfs/api.go @@ -10,7 +10,8 @@ import ( // A VFS defines the interface between the SQLite core and the underlying operating system. // -// Use sqlite3.ErrorCode or sqlite3.ExtendedErrorCode to return specific error codes to SQLite. +// Use [SystemError], sqlite3.ErrorCode, or sqlite3.ExtendedErrorCode +// to return specific error codes to SQLite. // // https://sqlite.org/c3ref/vfs.html type VFS interface { @@ -31,8 +32,9 @@ type VFSFilename interface { // A File represents an open file in the OS interface layer. // -// Use sqlite3.ErrorCode or sqlite3.ExtendedErrorCode to return specific error codes to SQLite. -// In particular, sqlite3.BUSY is necessary to correctly implement lock methods. +// Use [SystemError], sqlite3.ErrorCode, or sqlite3.ExtendedErrorCode +// to return specific error codes to SQLite. +// In particular, sqlite3.BUSY is needed to correctly implement lock methods. // // https://sqlite.org/c3ref/io_methods.html type File interface { diff --git a/vfs/vfs.go b/vfs/vfs.go index 49ca25b1..569346fb 100644 --- a/vfs/vfs.go +++ b/vfs/vfs.go @@ -477,6 +477,8 @@ func vfsErrorCode(ctx context.Context, err error, code _ErrorCode) _ErrorCode { return code } +// SystemError tags an error with a given +// sqlite3.ErrorCode or sqlite3.ExtendedErrorCode. func SystemError[T interface{ ~uint8 | ~uint16 }](err error, code T) error { if err == nil { return nil