From b749b32a62b20c275a51775c12fd69cf1057518b Mon Sep 17 00:00:00 2001 From: Nuno Cruces Date: Tue, 21 Feb 2023 12:51:52 +0000 Subject: [PATCH] Unlock tweaks, tests. --- driver/driver.go | 20 ++++++-------------- vfs_lock.go | 3 ++- vfs_test.go | 23 ++++++++++++++++++++++- vfs_unix.go | 19 ++++++++++--------- vfs_windows.go | 36 ++++++++++++++++++++++++------------ 5 files changed, 64 insertions(+), 37 deletions(-) diff --git a/driver/driver.go b/driver/driver.go index 85c2bfe..3142caa 100644 --- a/driver/driver.go +++ b/driver/driver.go @@ -83,8 +83,12 @@ func (c conn) Close() error { func (c conn) IsValid() bool { // Pool only normal locking mode connections. - mode, _ := pragma(c.conn, "locking_mode") - return mode == "normal" + stmt, _, err := c.conn.Prepare(`PRAGMA locking_mode`) + if err != nil { + return false + } + defer stmt.Close() + return stmt.Step() && stmt.ColumnText(0) == "normal" } func (c conn) ResetSession(ctx context.Context) error { @@ -175,18 +179,6 @@ func (c conn) ExecContext(ctx context.Context, query string, args []driver.Named }, nil } -func pragma(c *sqlite3.Conn, pragma string) (string, error) { - stmt, _, err := c.Prepare(`PRAGMA ` + pragma) - if err != nil { - return "", err - } - defer stmt.Close() - if stmt.Step() { - return stmt.ColumnText(0), nil - } - return "", stmt.Err() -} - type stmt struct { stmt *sqlite3.Stmt conn *sqlite3.Conn diff --git a/vfs_lock.go b/vfs_lock.go index da46a67..a247bbf 100644 --- a/vfs_lock.go +++ b/vfs_lock.go @@ -242,8 +242,9 @@ func vfsUnlock(ctx context.Context, mod api.Module, pFile uint32, eLock vfsLockS // Release the file lock only when all connections have released the lock. ptr.SetLock(_NO_LOCK) if fLock.shared--; fLock.shared == 0 { + rc := fLock.Release() fLock.state = _NO_LOCK - return uint32(fLock.Release()) + return uint32(rc) } return _OK } diff --git a/vfs_test.go b/vfs_test.go index 7262990..6b9aadc 100644 --- a/vfs_test.go +++ b/vfs_test.go @@ -7,6 +7,7 @@ import ( "io/fs" "os" "path/filepath" + "syscall" "testing" "time" @@ -163,8 +164,19 @@ func Test_vfsDelete(t *testing.T) { } func Test_vfsAccess(t *testing.T) { + dir := t.TempDir() + file := filepath.Join(t.TempDir(), "test.db") + if f, err := os.Create(file); err != nil { + t.Fatal(err) + } else { + f.Close() + } + if err := os.Chmod(file, syscall.S_IRUSR); err != nil { + t.Fatal(err) + } + mem := newMemory(128 + _MAX_PATHNAME) - mem.writeString(8, t.TempDir()) + mem.writeString(8, dir) rc := vfsAccess(context.TODO(), mem.mod, 0, 8, _ACCESS_EXISTS, 4) if rc != _OK { @@ -181,6 +193,15 @@ func Test_vfsAccess(t *testing.T) { if got := mem.readUint32(4); got != 1 { t.Error("can't access directory") } + + mem.writeString(8, file) + rc = vfsAccess(context.TODO(), mem.mod, 0, 8, _ACCESS_READWRITE, 4) + if rc != _OK { + t.Fatal("returned", rc) + } + if got := mem.readUint32(4); got != 0 { + t.Error("can access file") + } } func Test_vfsFile(t *testing.T) { diff --git a/vfs_unix.go b/vfs_unix.go index 0d8c709..16a9028 100644 --- a/vfs_unix.go +++ b/vfs_unix.go @@ -33,16 +33,17 @@ func (l *vfsFileLocker) GetExclusive() xErrorCode { } func (l *vfsFileLocker) Downgrade() xErrorCode { - // Downgrade to a SHARED lock. - if rc := l.readLock(_SHARED_FIRST, _SHARED_SIZE); 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 - // protocol. If this happens, return IOERR_RDLOCK. Returning - // BUSY would confuse the upper layer. - return IOERR_RDLOCK + if l.state >= _EXCLUSIVE_LOCK { + // Downgrade to a SHARED lock. + if rc := l.readLock(_SHARED_FIRST, _SHARED_SIZE); 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 + // protocol. If this happens, return IOERR_RDLOCK. Returning + // BUSY would confuse the upper layer. + return IOERR_RDLOCK + } } - // Release the PENDING and RESERVED locks. return l.unlock(_PENDING_BYTE, 2) } diff --git a/vfs_windows.go b/vfs_windows.go index e0d073f..24e9e6b 100644 --- a/vfs_windows.go +++ b/vfs_windows.go @@ -39,27 +39,39 @@ func (l *vfsFileLocker) GetExclusive() xErrorCode { } func (l *vfsFileLocker) Downgrade() xErrorCode { - // Release the SHARED lock. - l.unlock(_SHARED_FIRST, _SHARED_SIZE) + if l.state >= _EXCLUSIVE_LOCK { + // Release the SHARED lock. + l.unlock(_SHARED_FIRST, _SHARED_SIZE) - // Reacquire the SHARED lock. - if rc := l.readLock(_SHARED_FIRST, _SHARED_SIZE); rc != _OK { - // This should never happen. - // We should always be able to reacquire the read lock. - return IOERR_RDLOCK + // Reacquire the SHARED lock. + if rc := l.readLock(_SHARED_FIRST, _SHARED_SIZE); rc != _OK { + // This should never happen. + // We should always be able to reacquire the read lock. + return IOERR_RDLOCK + } } // Release the PENDING and RESERVED locks. - l.unlock(_RESERVED_BYTE, 1) - l.unlock(_PENDING_BYTE, 1) + if l.state >= _RESERVED_LOCK { + l.unlock(_RESERVED_BYTE, 1) + } + if l.state >= _PENDING_LOCK { + l.unlock(_PENDING_BYTE, 1) + } return _OK } func (l *vfsFileLocker) Release() xErrorCode { // Release all locks. - l.unlock(_SHARED_FIRST, _SHARED_SIZE) - l.unlock(_RESERVED_BYTE, 1) - l.unlock(_PENDING_BYTE, 1) + if l.state >= _RESERVED_LOCK { + l.unlock(_RESERVED_BYTE, 1) + } + if l.state >= _SHARED_LOCK { + l.unlock(_SHARED_FIRST, _SHARED_SIZE) + } + if l.state >= _PENDING_LOCK { + l.unlock(_PENDING_BYTE, 1) + } return _OK }