Unlock tweaks, tests.

This commit is contained in:
Nuno Cruces
2023-02-21 12:51:52 +00:00
parent 3b4df71a94
commit b749b32a62
5 changed files with 64 additions and 37 deletions

View File

@@ -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

View File

@@ -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
}

View File

@@ -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) {

View File

@@ -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)
}

View File

@@ -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
}