Require OFD locks.

This commit is contained in:
Nuno Cruces
2023-02-23 13:29:51 +00:00
parent 5b2451c3ad
commit 1ed62d300d
7 changed files with 161 additions and 309 deletions

View File

@@ -73,6 +73,7 @@ const (
noGlobalErr = errorString("sqlite3: could not find global: ")
noFuncErr = errorString("sqlite3: could not find function: ")
timeErr = errorString("sqlite3: invalid time value")
notImplErr = errorString("sqlite3: not implemented")
)
func assertErr() errorString {

20
vfs.go
View File

@@ -52,6 +52,10 @@ func vfsInstantiate(ctx context.Context, r wazero.Runtime) {
}
}
type vfsOSMethods bool
const vfsOS vfsOSMethods = false
func vfsExit(ctx context.Context, mod api.Module, exitCode uint32) {
// Ensure other callers see the exit code.
_ = mod.CloseWithExitCode(ctx, exitCode)
@@ -220,20 +224,10 @@ func vfsOpen(ctx context.Context, mod api.Module, pVfs, zName, pFile uint32, fla
}
if flags&OPEN_DELETEONCLOSE != 0 {
deleteOnClose(file)
vfsOS.DeleteOnClose(file)
}
var info fs.FileInfo
if flags&OPEN_MAIN_DB != 0 {
info, err = file.Stat()
if err != nil {
return uint32(CANTOPEN)
}
if info.IsDir() {
return uint32(CANTOPEN_ISDIR)
}
}
id := vfsGetOpenFileID(file, info)
id := vfsGetFileID(file)
vfsFilePtr{mod, pFile}.SetID(id).SetLock(_NO_LOCK)
if pOutFlags != 0 {
@@ -244,7 +238,7 @@ func vfsOpen(ctx context.Context, mod api.Module, pVfs, zName, pFile uint32, fla
func vfsClose(ctx context.Context, mod api.Module, pFile uint32) uint32 {
id := vfsFilePtr{mod, pFile}.ID()
err := vfsReleaseOpenFile(id)
err := vfsCloseFile(id)
if err != nil {
return uint32(IOERR_CLOSE)
}

View File

@@ -7,68 +7,35 @@ import (
"github.com/tetratelabs/wazero/api"
)
type vfsOpenFile struct {
file *os.File
info os.FileInfo
nref int
locker vfsFileLocker
}
var (
vfsOpenFiles []*vfsOpenFile
vfsOpenFiles []*os.File
vfsOpenFilesMtx sync.Mutex
)
func vfsGetOpenFileID(file *os.File, info os.FileInfo) uint32 {
func vfsGetFileID(file *os.File) uint32 {
vfsOpenFilesMtx.Lock()
defer vfsOpenFilesMtx.Unlock()
// Reuse an already opened file.
if info != nil {
for id, of := range vfsOpenFiles {
if of == nil {
continue
}
if os.SameFile(info, of.info) {
of.nref++
_ = file.Close()
return uint32(id)
}
}
}
of := &vfsOpenFile{
file: file,
info: info,
nref: 1,
locker: vfsFileLocker{file: file},
}
// Find an empty slot.
for id, ptr := range vfsOpenFiles {
if ptr == nil {
vfsOpenFiles[id] = of
vfsOpenFiles[id] = file
return uint32(id)
}
}
// Add a new slot.
id := len(vfsOpenFiles)
vfsOpenFiles = append(vfsOpenFiles, of)
return uint32(id)
vfsOpenFiles = append(vfsOpenFiles, file)
return uint32(len(vfsOpenFiles) - 1)
}
func vfsReleaseOpenFile(id uint32) error {
func vfsCloseFile(id uint32) error {
vfsOpenFilesMtx.Lock()
defer vfsOpenFilesMtx.Unlock()
of := vfsOpenFiles[id]
if of.nref--; of.nref > 0 {
return nil
}
err := of.file.Close()
file := vfsOpenFiles[id]
vfsOpenFiles[id] = nil
return err
return file.Close()
}
type vfsFilePtr struct {
@@ -80,14 +47,7 @@ func (p vfsFilePtr) OSFile() *os.File {
id := p.ID()
vfsOpenFilesMtx.Lock()
defer vfsOpenFilesMtx.Unlock()
return vfsOpenFiles[id].file
}
func (p vfsFilePtr) Locker() *vfsFileLocker {
id := p.ID()
vfsOpenFilesMtx.Lock()
defer vfsOpenFilesMtx.Unlock()
return &vfsOpenFiles[id].locker
return vfsOpenFiles[id]
}
func (p vfsFilePtr) ID() uint32 {

View File

@@ -3,7 +3,6 @@ package sqlite3
import (
"context"
"os"
"sync"
"github.com/tetratelabs/wazero/api"
)
@@ -56,13 +55,6 @@ const (
type vfsLockState uint32
type vfsFileLocker struct {
sync.Mutex
file *os.File
state vfsLockState
shared int
}
func vfsLock(ctx context.Context, mod api.Module, pFile uint32, eLock vfsLockState) uint32 {
// Argument check. SQLite never explicitly requests a pendig lock.
if eLock != _SHARED_LOCK && eLock != _RESERVED_LOCK && eLock != _EXCLUSIVE_LOCK {
@@ -70,6 +62,7 @@ func vfsLock(ctx context.Context, mod api.Module, pFile uint32, eLock vfsLockSta
}
ptr := vfsFilePtr{mod, pFile}
file := ptr.OSFile()
cLock := ptr.Lock()
switch {
@@ -89,93 +82,49 @@ func vfsLock(ctx context.Context, mod api.Module, pFile uint32, eLock vfsLockSta
return _OK
}
fLock := ptr.Locker()
fLock.Lock()
defer fLock.Unlock()
// File state check.
switch {
case fLock.state < _NO_LOCK || fLock.state > _EXCLUSIVE_LOCK:
panic(assertErr())
case fLock.state == _NO_LOCK && fLock.shared != 0:
panic(assertErr())
case fLock.state == _EXCLUSIVE_LOCK && fLock.shared != 1:
panic(assertErr())
case fLock.state != _NO_LOCK && fLock.shared <= 0:
panic(assertErr())
case fLock.state < cLock:
panic(assertErr())
}
// If some other connection has a lock that precludes the requested lock, return BUSY.
if cLock != fLock.state && (eLock > _SHARED_LOCK || fLock.state >= _PENDING_LOCK) {
return uint32(BUSY)
}
switch eLock {
case _SHARED_LOCK:
// Test the PENDING lock before acquiring a new SHARED lock.
if locked, _ := fLock.CheckPending(); locked {
return uint32(BUSY)
}
// If some other connection has a SHARED or RESERVED lock,
// increment the reference count and return OK.
if fLock.state == _SHARED_LOCK || fLock.state == _RESERVED_LOCK {
ptr.SetLock(_SHARED_LOCK)
fLock.shared++
return _OK
}
// Must be unlocked to get SHARED.
if fLock.state != _NO_LOCK {
if cLock != _NO_LOCK {
panic(assertErr())
}
if rc := fLock.GetShared(); rc != _OK {
// Test the PENDING lock before acquiring a new SHARED lock.
if locked, _ := vfsOS.CheckPendingLock(file); locked {
return uint32(BUSY)
}
if rc := vfsOS.GetSharedLock(file); rc != _OK {
return uint32(rc)
}
ptr.SetLock(_SHARED_LOCK)
fLock.state = _SHARED_LOCK
fLock.shared = 1
return _OK
case _RESERVED_LOCK:
// Must be SHARED to get RESERVED.
if fLock.state != _SHARED_LOCK {
if cLock != _SHARED_LOCK {
panic(assertErr())
}
if rc := fLock.GetReserved(); rc != _OK {
if rc := vfsOS.GetReservedLock(file); rc != _OK {
return uint32(rc)
}
ptr.SetLock(_RESERVED_LOCK)
fLock.state = _RESERVED_LOCK
return _OK
case _EXCLUSIVE_LOCK:
// Must be SHARED, PENDING or RESERVED to get EXCLUSIVE.
if fLock.state <= _NO_LOCK || fLock.state >= _EXCLUSIVE_LOCK {
// Must be SHARED, RESERVED or PENDING to get EXCLUSIVE.
if cLock <= _NO_LOCK || cLock >= _EXCLUSIVE_LOCK {
panic(assertErr())
}
// A PENDING lock is needed before acquiring an EXCLUSIVE lock.
if fLock.state == _RESERVED_LOCK {
if rc := fLock.GetPending(); rc != _OK {
if cLock == _RESERVED_LOCK {
if rc := vfsOS.GetPendingLock(file); rc != _OK {
return uint32(rc)
}
ptr.SetLock(_PENDING_LOCK)
fLock.state = _PENDING_LOCK
}
// We are trying for an EXCLUSIVE lock but another connection is still holding a shared lock.
if fLock.shared > 1 {
return uint32(BUSY)
}
if rc := fLock.GetExclusive(); rc != _OK {
if rc := vfsOS.GetExclusiveLock(file); rc != _OK {
return uint32(rc)
}
ptr.SetLock(_EXCLUSIVE_LOCK)
fLock.state = _EXCLUSIVE_LOCK
return _OK
default:
@@ -190,6 +139,7 @@ func vfsUnlock(ctx context.Context, mod api.Module, pFile uint32, eLock vfsLockS
}
ptr := vfsFilePtr{mod, pFile}
file := ptr.OSFile()
cLock := ptr.Lock()
// Connection state check.
@@ -202,51 +152,22 @@ func vfsUnlock(ctx context.Context, mod api.Module, pFile uint32, eLock vfsLockS
return _OK
}
fLock := ptr.Locker()
fLock.Lock()
defer fLock.Unlock()
// File state check.
switch {
case fLock.state <= _NO_LOCK || fLock.state > _EXCLUSIVE_LOCK:
panic(assertErr())
case fLock.state == _EXCLUSIVE_LOCK && fLock.shared != 1:
panic(assertErr())
case fLock.shared <= 0:
panic(assertErr())
case fLock.state < cLock:
panic(assertErr())
}
if cLock > _SHARED_LOCK {
// The connection must own the lock to release it.
if cLock != fLock.state {
panic(assertErr())
switch eLock {
case _SHARED_LOCK:
if rc := vfsOS.DowngradeLock(file, cLock); rc != _OK {
return uint32(rc)
}
if eLock == _SHARED_LOCK {
if rc := fLock.Downgrade(); rc != _OK {
return uint32(rc)
}
ptr.SetLock(_SHARED_LOCK)
fLock.state = _SHARED_LOCK
return _OK
}
}
ptr.SetLock(_SHARED_LOCK)
return _OK
// If we get here, make sure we're dropping all locks.
if eLock != _NO_LOCK {
panic(assertErr())
}
// Release the connection lock and decrement the shared lock counter.
// 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
case _NO_LOCK:
rc := vfsOS.ReleaseLock(file, cLock)
ptr.SetLock(_NO_LOCK)
return uint32(rc)
default:
panic(assertErr())
}
return _OK
}
func vfsCheckReservedLock(ctx context.Context, mod api.Module, pFile, pResOut uint32) uint32 {
@@ -257,16 +178,9 @@ func vfsCheckReservedLock(ctx context.Context, mod api.Module, pFile, pResOut ui
panic(assertErr())
}
fLock := ptr.Locker()
fLock.Lock()
defer fLock.Unlock()
file := ptr.OSFile()
if fLock.state >= _RESERVED_LOCK {
memory{mod}.writeUint32(pResOut, 1)
return _OK
}
locked, rc := fLock.CheckReserved()
locked, rc := vfsOS.CheckReservedLock(file)
var res uint32
if locked {
res = 1
@@ -274,3 +188,28 @@ func vfsCheckReservedLock(ctx context.Context, mod api.Module, pFile, pResOut ui
memory{mod}.writeUint32(pResOut, res)
return uint32(rc)
}
func (vfsOSMethods) GetSharedLock(file *os.File) xErrorCode {
// Acquire the SHARED lock.
return vfsOS.readLock(file, _SHARED_FIRST, _SHARED_SIZE)
}
func (vfsOSMethods) GetReservedLock(file *os.File) xErrorCode {
// Acquire the RESERVED lock.
return vfsOS.writeLock(file, _RESERVED_BYTE, 1)
}
func (vfsOSMethods) GetPendingLock(file *os.File) xErrorCode {
// Acquire the PENDING lock.
return vfsOS.writeLock(file, _PENDING_BYTE, 1)
}
func (vfsOSMethods) CheckReservedLock(file *os.File) (bool, xErrorCode) {
// Test the RESERVED lock.
return vfsOS.checkLock(file, _RESERVED_BYTE, 1)
}
func (vfsOSMethods) CheckPendingLock(file *os.File) (bool, xErrorCode) {
// Test the PENDING lock.
return vfsOS.checkLock(file, _PENDING_BYTE, 1)
}

View File

@@ -12,7 +12,7 @@ func Test_vfsLock(t *testing.T) {
// Other OSes lack open file descriptors locks.
switch runtime.GOOS {
case "linux", "darwin", "illumos", "windows":
//
break
default:
t.Skip()
}
@@ -33,26 +33,14 @@ func Test_vfsLock(t *testing.T) {
}
defer file2.Close()
// Bypass open file reuse.
vfsOpenFiles = append(vfsOpenFiles, &vfsOpenFile{
file: file1,
nref: 1,
locker: vfsFileLocker{file: file1},
}, &vfsOpenFile{
file: file2,
nref: 1,
locker: vfsFileLocker{file: file2},
})
mem := newMemory(128)
mem.writeUint32(4+4, 0)
mem.writeUint32(16+4, 1)
const (
pFile1 = 4
pFile2 = 16
pOutput = 32
)
mem := newMemory(128)
vfsFilePtr{mem.mod, pFile1}.SetID(vfsGetFileID(file1)).SetLock(_NO_LOCK)
vfsFilePtr{mem.mod, pFile2}.SetID(vfsGetFileID(file2)).SetLock(_NO_LOCK)
rc := vfsCheckReservedLock(context.TODO(), mem.mod, pFile1, pOutput)
if rc != _OK {
@@ -110,11 +98,27 @@ func Test_vfsLock(t *testing.T) {
t.Fatal("returned", rc)
}
rc = vfsCheckReservedLock(context.TODO(), mem.mod, pFile1, pOutput)
if rc != _OK {
t.Fatal("returned", rc)
}
if got := mem.readUint32(pOutput); got == 0 {
t.Error("file wasn't locked")
}
rc = vfsUnlock(context.TODO(), mem.mod, pFile2, _SHARED_LOCK)
if rc != _OK {
t.Fatal("returned", rc)
}
rc = vfsCheckReservedLock(context.TODO(), mem.mod, pFile1, pOutput)
if rc != _OK {
t.Fatal("returned", rc)
}
if got := mem.readUint32(pOutput); got != 0 {
t.Error("file was locked")
}
rc = vfsLock(context.TODO(), mem.mod, pFile1, _SHARED_LOCK)
if rc != _OK {
t.Fatal("returned", rc)

View File

@@ -8,34 +8,19 @@ import (
"syscall"
)
func deleteOnClose(f *os.File) {
_ = os.Remove(f.Name())
func (vfsOSMethods) DeleteOnClose(file *os.File) {
_ = os.Remove(file.Name())
}
func (l *vfsFileLocker) GetShared() xErrorCode {
// Acquire the SHARED lock.
return l.readLock(_SHARED_FIRST, _SHARED_SIZE)
}
func (l *vfsFileLocker) GetReserved() xErrorCode {
// Acquire the RESERVED lock.
return l.writeLock(_RESERVED_BYTE, 1)
}
func (l *vfsFileLocker) GetPending() xErrorCode {
// Acquire the PENDING lock.
return l.writeLock(_PENDING_BYTE, 1)
}
func (l *vfsFileLocker) GetExclusive() xErrorCode {
func (vfsOSMethods) GetExclusiveLock(file *os.File) xErrorCode {
// Acquire the EXCLUSIVE lock.
return l.writeLock(_SHARED_FIRST, _SHARED_SIZE)
return vfsOS.writeLock(file, _SHARED_FIRST, _SHARED_SIZE)
}
func (l *vfsFileLocker) Downgrade() xErrorCode {
if l.state >= _EXCLUSIVE_LOCK {
func (vfsOSMethods) DowngradeLock(file *os.File, state vfsLockState) xErrorCode {
if state >= _EXCLUSIVE_LOCK {
// Downgrade to a SHARED lock.
if rc := l.readLock(_SHARED_FIRST, _SHARED_SIZE); rc != _OK {
if rc := vfsOS.readLock(file, _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
@@ -45,26 +30,16 @@ func (l *vfsFileLocker) Downgrade() xErrorCode {
}
}
// Release the PENDING and RESERVED locks.
return l.unlock(_PENDING_BYTE, 2)
return vfsOS.unlock(file, _PENDING_BYTE, 2)
}
func (l *vfsFileLocker) Release() xErrorCode {
func (vfsOSMethods) ReleaseLock(file *os.File, _ vfsLockState) xErrorCode {
// Release all locks.
return l.unlock(0, 0)
return vfsOS.unlock(file, 0, 0)
}
func (l *vfsFileLocker) CheckReserved() (bool, xErrorCode) {
// Test the RESERVED lock.
return l.checkLock(_RESERVED_BYTE, 1)
}
func (l *vfsFileLocker) CheckPending() (bool, xErrorCode) {
// Test the PENDING lock.
return l.checkLock(_PENDING_BYTE, 1)
}
func (l *vfsFileLocker) unlock(start, len int64) xErrorCode {
err := l.fcntlSetLock(&syscall.Flock_t{
func (vfsOSMethods) unlock(file *os.File, start, len int64) xErrorCode {
err := vfsOS.fcntlSetLock(file, &syscall.Flock_t{
Type: syscall.F_UNLCK,
Start: start,
Len: len,
@@ -75,67 +50,71 @@ func (l *vfsFileLocker) unlock(start, len int64) xErrorCode {
return _OK
}
func (l *vfsFileLocker) readLock(start, len int64) xErrorCode {
return l.errorCode(l.fcntlSetLock(&syscall.Flock_t{
func (vfsOSMethods) readLock(file *os.File, start, len int64) xErrorCode {
return vfsOS.lockErrorCode(vfsOS.fcntlSetLock(file, &syscall.Flock_t{
Type: syscall.F_RDLCK,
Start: start,
Len: len,
}), IOERR_RDLOCK)
}
func (l *vfsFileLocker) writeLock(start, len int64) xErrorCode {
return l.errorCode(l.fcntlSetLock(&syscall.Flock_t{
func (vfsOSMethods) writeLock(file *os.File, start, len int64) xErrorCode {
return vfsOS.lockErrorCode(vfsOS.fcntlSetLock(file, &syscall.Flock_t{
Type: syscall.F_WRLCK,
Start: start,
Len: len,
}), IOERR_LOCK)
}
func (l *vfsFileLocker) checkLock(start, len int64) (bool, xErrorCode) {
func (vfsOSMethods) checkLock(file *os.File, start, len int64) (bool, xErrorCode) {
lock := syscall.Flock_t{
Type: syscall.F_RDLCK,
Start: start,
Len: len,
}
if l.fcntlGetLock(&lock) != nil {
if vfsOS.fcntlGetLock(file, &lock) != nil {
return false, IOERR_CHECKRESERVEDLOCK
}
return lock.Type != syscall.F_UNLCK, _OK
}
func (l *vfsFileLocker) fcntlGetLock(lock *syscall.Flock_t) error {
F_GETLK := syscall.F_GETLK
func (vfsOSMethods) fcntlGetLock(file *os.File, lock *syscall.Flock_t) error {
var F_OFD_GETLK int
switch runtime.GOOS {
case "linux":
// https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/fcntl.h
F_GETLK = 36 // F_OFD_GETLK
F_OFD_GETLK = 36 // F_OFD_GETLK
case "darwin":
// https://github.com/apple/darwin-xnu/blob/main/bsd/sys/fcntl.h
F_GETLK = 92 // F_OFD_GETLK
F_OFD_GETLK = 92 // F_OFD_GETLK
case "illumos":
// https://github.com/illumos/illumos-gate/blob/master/usr/src/uts/common/sys/fcntl.h
F_GETLK = 47 // F_OFD_GETLK
F_OFD_GETLK = 47 // F_OFD_GETLK
default:
return notImplErr
}
return syscall.FcntlFlock(l.file.Fd(), F_GETLK, lock)
return syscall.FcntlFlock(file.Fd(), F_OFD_GETLK, lock)
}
func (l *vfsFileLocker) fcntlSetLock(lock *syscall.Flock_t) error {
F_SETLK := syscall.F_SETLK
func (vfsOSMethods) fcntlSetLock(file *os.File, lock *syscall.Flock_t) error {
var F_OFD_SETLK int
switch runtime.GOOS {
case "linux":
// https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/fcntl.h
F_SETLK = 37 // F_OFD_SETLK
F_OFD_SETLK = 37 // F_OFD_SETLK
case "darwin":
// https://github.com/apple/darwin-xnu/blob/main/bsd/sys/fcntl.h
F_SETLK = 90 // F_OFD_SETLK
F_OFD_SETLK = 90 // F_OFD_SETLK
case "illumos":
// https://github.com/illumos/illumos-gate/blob/master/usr/src/uts/common/sys/fcntl.h
F_SETLK = 48 // F_OFD_SETLK
F_OFD_SETLK = 48 // F_OFD_SETLK
default:
return notImplErr
}
return syscall.FcntlFlock(l.file.Fd(), F_SETLK, lock)
return syscall.FcntlFlock(file.Fd(), F_OFD_SETLK, lock)
}
func (*vfsFileLocker) errorCode(err error, def xErrorCode) xErrorCode {
func (vfsOSMethods) lockErrorCode(err error, def xErrorCode) xErrorCode {
if err == nil {
return _OK
}

View File

@@ -7,44 +7,29 @@ import (
"golang.org/x/sys/windows"
)
func deleteOnClose(f *os.File) {}
func (vfsOSMethods) DeleteOnClose(file *os.File) {}
func (l *vfsFileLocker) GetShared() xErrorCode {
// Acquire the SHARED lock.
return l.readLock(_SHARED_FIRST, _SHARED_SIZE)
}
func (l *vfsFileLocker) GetReserved() xErrorCode {
// Acquire the RESERVED lock.
return l.writeLock(_RESERVED_BYTE, 1)
}
func (l *vfsFileLocker) GetPending() xErrorCode {
// Acquire the PENDING lock.
return l.writeLock(_PENDING_BYTE, 1)
}
func (l *vfsFileLocker) GetExclusive() xErrorCode {
func (vfsOSMethods) GetExclusiveLock(file *os.File) xErrorCode {
// Release the SHARED lock.
l.unlock(_SHARED_FIRST, _SHARED_SIZE)
vfsOS.unlock(file, _SHARED_FIRST, _SHARED_SIZE)
// Acquire the EXCLUSIVE lock.
rc := l.writeLock(_SHARED_FIRST, _SHARED_SIZE)
rc := vfsOS.writeLock(file, _SHARED_FIRST, _SHARED_SIZE)
// Reacquire the SHARED lock.
if rc != _OK {
l.readLock(_SHARED_FIRST, _SHARED_SIZE)
vfsOS.readLock(file, _SHARED_FIRST, _SHARED_SIZE)
}
return rc
}
func (l *vfsFileLocker) Downgrade() xErrorCode {
if l.state >= _EXCLUSIVE_LOCK {
func (vfsOSMethods) DowngradeLock(file *os.File, state vfsLockState) xErrorCode {
if state >= _EXCLUSIVE_LOCK {
// Release the SHARED lock.
l.unlock(_SHARED_FIRST, _SHARED_SIZE)
vfsOS.unlock(file, _SHARED_FIRST, _SHARED_SIZE)
// Reacquire the SHARED lock.
if rc := l.readLock(_SHARED_FIRST, _SHARED_SIZE); rc != _OK {
if rc := vfsOS.readLock(file, _SHARED_FIRST, _SHARED_SIZE); rc != _OK {
// This should never happen.
// We should always be able to reacquire the read lock.
return IOERR_RDLOCK
@@ -52,49 +37,31 @@ func (l *vfsFileLocker) Downgrade() xErrorCode {
}
// Release the PENDING and RESERVED locks.
if l.state >= _RESERVED_LOCK {
l.unlock(_RESERVED_BYTE, 1)
if state >= _RESERVED_LOCK {
vfsOS.unlock(file, _RESERVED_BYTE, 1)
}
if l.state >= _PENDING_LOCK {
l.unlock(_PENDING_BYTE, 1)
if state >= _PENDING_LOCK {
vfsOS.unlock(file, _PENDING_BYTE, 1)
}
return _OK
}
func (l *vfsFileLocker) Release() xErrorCode {
func (vfsOSMethods) ReleaseLock(file *os.File, state vfsLockState) xErrorCode {
// Release all locks.
if l.state >= _RESERVED_LOCK {
l.unlock(_RESERVED_BYTE, 1)
if state >= _RESERVED_LOCK {
vfsOS.unlock(file, _RESERVED_BYTE, 1)
}
if l.state >= _SHARED_LOCK {
l.unlock(_SHARED_FIRST, _SHARED_SIZE)
if state >= _SHARED_LOCK {
vfsOS.unlock(file, _SHARED_FIRST, _SHARED_SIZE)
}
if l.state >= _PENDING_LOCK {
l.unlock(_PENDING_BYTE, 1)
if state >= _PENDING_LOCK {
vfsOS.unlock(file, _PENDING_BYTE, 1)
}
return _OK
}
func (l *vfsFileLocker) CheckReserved() (bool, xErrorCode) {
// Test the RESERVED lock.
rc := l.readLock(_RESERVED_BYTE, 1)
if rc == _OK {
l.unlock(_RESERVED_BYTE, 1)
}
return rc != _OK, _OK
}
func (l *vfsFileLocker) CheckPending() (bool, xErrorCode) {
// Test the PENDING lock.
rc := l.readLock(_PENDING_BYTE, 1)
if rc == _OK {
l.unlock(_PENDING_BYTE, 1)
}
return rc != _OK, _OK
}
func (l *vfsFileLocker) unlock(start, len uint32) xErrorCode {
err := windows.UnlockFileEx(windows.Handle(l.file.Fd()),
func (vfsOSMethods) unlock(file *os.File, start, len uint32) xErrorCode {
err := windows.UnlockFileEx(windows.Handle(file.Fd()),
0, len, 0, &windows.Overlapped{Offset: start})
if err != nil {
return IOERR_UNLOCK
@@ -102,21 +69,29 @@ func (l *vfsFileLocker) unlock(start, len uint32) xErrorCode {
return _OK
}
func (l *vfsFileLocker) readLock(start, len uint32) xErrorCode {
return l.errorCode(windows.LockFileEx(windows.Handle(l.file.Fd()),
func (vfsOSMethods) readLock(file *os.File, start, len uint32) xErrorCode {
return vfsOS.lockErrorCode(windows.LockFileEx(windows.Handle(file.Fd()),
windows.LOCKFILE_FAIL_IMMEDIATELY,
0, len, 0, &windows.Overlapped{Offset: start}),
IOERR_RDLOCK)
}
func (l *vfsFileLocker) writeLock(start, len uint32) xErrorCode {
return l.errorCode(windows.LockFileEx(windows.Handle(l.file.Fd()),
func (vfsOSMethods) writeLock(file *os.File, start, len uint32) xErrorCode {
return vfsOS.lockErrorCode(windows.LockFileEx(windows.Handle(file.Fd()),
windows.LOCKFILE_FAIL_IMMEDIATELY|windows.LOCKFILE_EXCLUSIVE_LOCK,
0, len, 0, &windows.Overlapped{Offset: start}),
IOERR_LOCK)
}
func (*vfsFileLocker) errorCode(err error, def xErrorCode) xErrorCode {
func (vfsOSMethods) checkLock(file *os.File, start, len uint32) (bool, xErrorCode) {
rc := vfsOS.readLock(file, start, len)
if rc == _OK {
vfsOS.unlock(file, start, len)
}
return rc != _OK, _OK
}
func (vfsOSMethods) lockErrorCode(err error, def xErrorCode) xErrorCode {
if err == nil {
return _OK
}