Unix fcntl locks.

This commit is contained in:
Nuno Cruces
2023-01-25 16:23:18 +00:00
parent 921e1eafc2
commit 0ea1ec8bb9
8 changed files with 179 additions and 37 deletions

View File

@@ -40,7 +40,7 @@ func OpenFlags(filename string, flags OpenFlag) (conn *Conn, err error) {
c := newConn(module)
c.ctx = context.Background()
namePtr := c.newString(filename)
connPtr := c.new(wordSize)
connPtr := c.new(ptrlen)
defer c.free(namePtr)
defer c.free(connPtr)
@@ -86,8 +86,8 @@ func (c *Conn) Prepare(sql string) (stmt *Stmt, tail string, err error) {
func (c *Conn) PrepareFlags(sql string, flags PrepareFlag) (stmt *Stmt, tail string, err error) {
sqlPtr := c.newString(sql)
stmtPtr := c.new(wordSize)
tailPtr := c.new(wordSize)
stmtPtr := c.new(ptrlen)
tailPtr := c.new(ptrlen)
defer c.free(sqlPtr)
defer c.free(stmtPtr)
defer c.free(tailPtr)
@@ -221,5 +221,3 @@ func getString(memory api.Memory, ptr, maxlen uint32) string {
return string(mem[:i])
}
}
const wordSize = 4

View File

@@ -8,6 +8,9 @@ const (
_UTF8 = 1
_MAX_PATHNAME = 512
assert = true
ptrlen = 4
)
type ErrorCode uint8

18
vfs.go
View File

@@ -67,15 +67,15 @@ func vfsLocaltime(ctx context.Context, mod api.Module, t uint64, pTm uint32) uin
}
// https://pubs.opengroup.org/onlinepubs/7908799/xsh/time.h.html
if mem := mod.Memory(); true &&
mem.WriteUint32Le(pTm+0*wordSize, uint32(tm.Second())) &&
mem.WriteUint32Le(pTm+1*wordSize, uint32(tm.Minute())) &&
mem.WriteUint32Le(pTm+2*wordSize, uint32(tm.Hour())) &&
mem.WriteUint32Le(pTm+3*wordSize, uint32(tm.Day())) &&
mem.WriteUint32Le(pTm+4*wordSize, uint32(tm.Month()-time.January)) &&
mem.WriteUint32Le(pTm+5*wordSize, uint32(tm.Year()-1900)) &&
mem.WriteUint32Le(pTm+6*wordSize, uint32(tm.Weekday()-time.Sunday)) &&
mem.WriteUint32Le(pTm+7*wordSize, uint32(tm.YearDay()-1)) &&
mem.WriteUint32Le(pTm+8*wordSize, uint32(isdst)) {
mem.WriteUint32Le(pTm+0*ptrlen, uint32(tm.Second())) &&
mem.WriteUint32Le(pTm+1*ptrlen, uint32(tm.Minute())) &&
mem.WriteUint32Le(pTm+2*ptrlen, uint32(tm.Hour())) &&
mem.WriteUint32Le(pTm+3*ptrlen, uint32(tm.Day())) &&
mem.WriteUint32Le(pTm+4*ptrlen, uint32(tm.Month()-time.January)) &&
mem.WriteUint32Le(pTm+5*ptrlen, uint32(tm.Year()-1900)) &&
mem.WriteUint32Le(pTm+6*ptrlen, uint32(tm.Weekday()-time.Sunday)) &&
mem.WriteUint32Le(pTm+7*ptrlen, uint32(tm.YearDay()-1)) &&
mem.WriteUint32Le(pTm+8*ptrlen, uint32(isdst)) {
return _OK
}
panic(rangeErr)

View File

@@ -42,7 +42,7 @@ func vfsGetOpenFileID(file *os.File, info os.FileInfo) uint32 {
info: info,
nref: 1,
vfsLocker: &vfsNoopLocker{},
vfsLocker: &vfsFileLocker{file, _NO_LOCK},
}
// Find an empty slot.
@@ -88,7 +88,7 @@ func (p vfsFilePtr) ID() uint32 {
if p.ptr == 0 {
panic(nilErr)
}
id, ok := p.Memory().ReadUint32Le(p.ptr + wordSize)
id, ok := p.Memory().ReadUint32Le(p.ptr + ptrlen)
if !ok {
panic(rangeErr)
}
@@ -99,7 +99,7 @@ func (p vfsFilePtr) Lock() vfsLockState {
if p.ptr == 0 {
panic(nilErr)
}
lk, ok := p.Memory().ReadUint32Le(p.ptr + 2*wordSize)
lk, ok := p.Memory().ReadUint32Le(p.ptr + 2*ptrlen)
if !ok {
panic(rangeErr)
}
@@ -110,7 +110,7 @@ func (p vfsFilePtr) SetID(id uint32) vfsFilePtr {
if p.ptr == 0 {
panic(nilErr)
}
if ok := p.Memory().WriteUint32Le(p.ptr+wordSize, id); !ok {
if ok := p.Memory().WriteUint32Le(p.ptr+ptrlen, id); !ok {
panic(rangeErr)
}
return p
@@ -120,7 +120,7 @@ func (p vfsFilePtr) SetLock(lock vfsLockState) vfsFilePtr {
if p.ptr == 0 {
panic(nilErr)
}
if ok := p.Memory().WriteUint32Le(p.ptr+2*wordSize, uint32(lock)); !ok {
if ok := p.Memory().WriteUint32Le(p.ptr+2*ptrlen, uint32(lock)); !ok {
panic(rangeErr)
}
return p

View File

@@ -52,19 +52,22 @@ const (
_SHARED_SIZE = 510
)
type vfsLockState uint32
type (
vfsLockState uint32
xErrorCode = ExtendedErrorCode
)
type vfsLocker interface {
LockState() vfsLockState
LockShared() uint32 // UNLOCKED -> SHARED
LockReserved() uint32 // SHARED -> RESERVED
LockPending() uint32 // SHARED|RESERVED -> PENDING
LockExclusive() uint32 // PENDING -> EXCLUSIVE
DowngradeLock() uint32 // SHARED <- EXCLUSIVE|PENDING|RESERVED
Unlock() uint32 // UNLOCKED <- EXCLUSIVE|PENDING|RESERVED|SHARED
LockShared() xErrorCode // UNLOCKED -> SHARED
LockReserved() xErrorCode // SHARED -> RESERVED
LockPending() xErrorCode // SHARED|RESERVED -> PENDING
LockExclusive() xErrorCode // PENDING -> EXCLUSIVE
DowngradeLock() xErrorCode // SHARED <- EXCLUSIVE|PENDING|RESERVED
Unlock() xErrorCode // UNLOCKED <- EXCLUSIVE|PENDING|RESERVED|SHARED
CheckReservedLock() (bool, uint32)
CheckReservedLock() (bool, xErrorCode)
}
func vfsLock(ctx context.Context, mod api.Module, pFile uint32, eLock vfsLockState) uint32 {

View File

@@ -1,7 +1,5 @@
package sqlite3
const assert = true
type vfsNoopLocker struct {
state vfsLockState
}
@@ -12,7 +10,7 @@ func (l *vfsNoopLocker) LockState() vfsLockState {
return l.state
}
func (l *vfsNoopLocker) LockShared() uint32 {
func (l *vfsNoopLocker) LockShared() xErrorCode {
if assert && !(l.state == _NO_LOCK) {
panic(assertErr + " [wz9dcw]")
}
@@ -20,7 +18,7 @@ func (l *vfsNoopLocker) LockShared() uint32 {
return _OK
}
func (l *vfsNoopLocker) LockReserved() uint32 {
func (l *vfsNoopLocker) LockReserved() xErrorCode {
if assert && !(l.state == _SHARED_LOCK) {
panic(assertErr + " [m9hcil]")
}
@@ -28,7 +26,7 @@ func (l *vfsNoopLocker) LockReserved() uint32 {
return _OK
}
func (l *vfsNoopLocker) LockPending() uint32 {
func (l *vfsNoopLocker) LockPending() xErrorCode {
if assert && !(l.state == _SHARED_LOCK || l.state == _RESERVED_LOCK) {
panic(assertErr + " [wx8nk2]")
}
@@ -36,7 +34,7 @@ func (l *vfsNoopLocker) LockPending() uint32 {
return _OK
}
func (l *vfsNoopLocker) LockExclusive() uint32 {
func (l *vfsNoopLocker) LockExclusive() xErrorCode {
if assert && !(l.state == _PENDING_LOCK) {
panic(assertErr + " [84nbax]")
}
@@ -44,7 +42,7 @@ func (l *vfsNoopLocker) LockExclusive() uint32 {
return _OK
}
func (l *vfsNoopLocker) DowngradeLock() uint32 {
func (l *vfsNoopLocker) DowngradeLock() xErrorCode {
if assert && !(l.state > _SHARED_LOCK) {
panic(assertErr + " [je31i3]")
}
@@ -52,7 +50,7 @@ func (l *vfsNoopLocker) DowngradeLock() uint32 {
return _OK
}
func (l *vfsNoopLocker) Unlock() uint32 {
func (l *vfsNoopLocker) Unlock() xErrorCode {
if assert && !(l.state > _NO_LOCK) {
panic(assertErr + " [m6e9w5]")
}
@@ -60,7 +58,7 @@ func (l *vfsNoopLocker) Unlock() uint32 {
return _OK
}
func (l *vfsNoopLocker) CheckReservedLock() (bool, uint32) {
func (l *vfsNoopLocker) CheckReservedLock() (bool, xErrorCode) {
if l.state >= _RESERVED_LOCK {
return true, _OK
}

View File

@@ -2,8 +2,146 @@
package sqlite3
import "os"
import (
"os"
"syscall"
)
func deleteOnClose(f *os.File) {
_ = os.Remove(f.Name())
}
type vfsFileLocker struct {
*os.File
state vfsLockState
}
func (l *vfsFileLocker) LockState() vfsLockState {
return l.state
}
func (l *vfsFileLocker) LockShared() xErrorCode {
// A PENDING lock is needed before acquiring a SHARED lock.
if !l.fcntlSetLock(&syscall.Flock_t{
Type: syscall.F_RDLCK,
Start: _PENDING_BYTE,
Len: 1,
}) {
return IOERR_LOCK
}
// Acquire the SHARED lock.
if !l.fcntlSetLock(&syscall.Flock_t{
Type: syscall.F_RDLCK,
Start: _SHARED_FIRST,
Len: _SHARED_SIZE,
}) {
return IOERR_LOCK
}
l.state = _SHARED_LOCK
// Relese the PENDING lock.
if !l.fcntlSetLock(&syscall.Flock_t{
Type: syscall.F_UNLCK,
Start: _PENDING_BYTE,
Len: 1,
}) {
return IOERR_UNLOCK
}
return _OK
}
func (l *vfsFileLocker) LockReserved() xErrorCode {
// Acquire the RESERVED lock.
if !l.fcntlSetLock(&syscall.Flock_t{
Type: syscall.F_WRLCK,
Start: _RESERVED_BYTE,
Len: 1,
}) {
return IOERR_LOCK
}
l.state = _RESERVED_LOCK
return _OK
}
func (l *vfsFileLocker) LockPending() xErrorCode {
// Acquire the PENDING lock.
if !l.fcntlSetLock(&syscall.Flock_t{
Type: syscall.F_WRLCK,
Start: _PENDING_BYTE,
Len: 1,
}) {
return IOERR_LOCK
}
l.state = _PENDING_LOCK
return _OK
}
func (l *vfsFileLocker) LockExclusive() xErrorCode {
// Acquire the EXCLUSIVE lock.
if !l.fcntlSetLock(&syscall.Flock_t{
Type: syscall.F_WRLCK,
Start: _SHARED_FIRST,
Len: _SHARED_SIZE,
}) {
return IOERR_LOCK
}
l.state = _EXCLUSIVE_LOCK
return _OK
}
func (l *vfsFileLocker) DowngradeLock() xErrorCode {
// Downgrade to a SHARED lock.
if !l.fcntlSetLock(&syscall.Flock_t{
Type: syscall.F_RDLCK,
Start: _SHARED_FIRST,
Len: _SHARED_SIZE,
}) {
return IOERR_RDLOCK
}
l.state = _SHARED_LOCK
// Release the PENDING and RESERVED locks.
if !l.fcntlSetLock(&syscall.Flock_t{
Type: syscall.F_UNLCK,
Start: _PENDING_BYTE,
Len: 2,
}) {
return IOERR_UNLOCK
}
return _OK
}
func (l *vfsFileLocker) Unlock() xErrorCode {
// Release all locks.
if !l.fcntlSetLock(&syscall.Flock_t{
Type: syscall.F_UNLCK,
}) {
return IOERR_UNLOCK
}
l.state = _NO_LOCK
return _OK
}
func (l *vfsFileLocker) CheckReservedLock() (bool, xErrorCode) {
if l.state >= _RESERVED_LOCK {
return true, _OK
}
// Test all write locks.
lock := syscall.Flock_t{
Type: syscall.F_RDLCK,
}
if !l.fcntlGetLock(&lock) {
return false, IOERR_CHECKRESERVEDLOCK
}
return lock.Type == syscall.F_UNLCK, _OK
}
func (l *vfsFileLocker) fcntlGetLock(lock *syscall.Flock_t) bool {
return syscall.FcntlFlock(l.Fd(), syscall.F_GETLK, lock) == nil
}
func (l *vfsFileLocker) fcntlSetLock(lock *syscall.Flock_t) bool {
return syscall.FcntlFlock(l.Fd(), syscall.F_SETLK, lock) == nil
}

View File

@@ -1,3 +1,5 @@
package sqlite3
func deleteOnClose(f *os.File) {}
type vfsFileLocker = vfsNoopLocker