Unix locks.

This commit is contained in:
Nuno Cruces
2023-02-07 15:04:42 +00:00
parent 89a8ebecc8
commit dfec8ecbae
3 changed files with 49 additions and 99 deletions

View File

@@ -64,6 +64,7 @@ type vfsFileLocker struct {
}
func vfsLock(ctx context.Context, mod api.Module, pFile uint32, eLock vfsLockState) uint32 {
// SQLite never explicitly requests a pendig lock.
if eLock != _SHARED_LOCK && eLock != _RESERVED_LOCK && eLock != _EXCLUSIVE_LOCK {
panic(assertErr())
}
@@ -93,10 +94,6 @@ func vfsLock(ctx context.Context, mod api.Module, pFile uint32, eLock vfsLockSta
if cLock != fLock.state && (eLock > _SHARED_LOCK || fLock.state >= _PENDING_LOCK) {
return uint32(BUSY)
}
// We are trying for an exclusive lock but another connection is still holding a shared lock.
if eLock == _EXCLUSIVE_LOCK && fLock.shared > 1 {
return uint32(BUSY)
}
// If a SHARED lock is requested, and some other connection has a SHARED or RESERVED lock,
// then increment the reference count and return OK.
@@ -109,46 +106,56 @@ func vfsLock(ctx context.Context, mod api.Module, pFile uint32, eLock vfsLockSta
return _OK
}
// Get PENDING lock before acquiring an EXCLUSIVE lock.
if eLock == _EXCLUSIVE_LOCK && cLock == _RESERVED_LOCK {
if rc := fLock.GetPending(); rc != _OK {
return uint32(rc)
}
ptr.SetLock(_PENDING_LOCK)
}
// If control gets to this point, then actually go ahead and make
// operating system calls for the specified lock.
switch eLock {
case _SHARED_LOCK:
if !(fLock.state == _NO_LOCK && fLock.shared == 0) {
if fLock.state != _NO_LOCK || fLock.shared != 0 {
panic(assertErr())
}
if rc := fLock.GetShared(); rc != _OK {
return uint32(rc)
}
ptr.SetLock(_SHARED_LOCK)
fLock.state = _SHARED_LOCK
fLock.shared = 1
return _OK
case _RESERVED_LOCK:
if !(fLock.state == _SHARED_LOCK && fLock.shared > 0) {
if fLock.state != _SHARED_LOCK || fLock.shared <= 0 {
panic(assertErr())
}
if rc := fLock.GetReserved(); rc != _OK {
return uint32(rc)
}
ptr.SetLock(_RESERVED_LOCK)
fLock.state = _RESERVED_LOCK
return _OK
case _EXCLUSIVE_LOCK:
if !(fLock.state != _NO_LOCK && fLock.shared > 0) {
if fLock.state <= _NO_LOCK || fLock.state >= _EXCLUSIVE_LOCK || fLock.shared <= 0 {
panic(assertErr())
}
// A PENDING lock is needed before acquiring an EXCLUSIVE lock.
if fLock.state == _RESERVED_LOCK {
if rc := fLock.GetPending(); 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 {
return uint32(rc)
}
ptr.SetLock(_EXCLUSIVE_LOCK)
fLock.state = _EXCLUSIVE_LOCK
return _OK
default:
@@ -185,6 +192,7 @@ func vfsUnlock(ctx context.Context, mod api.Module, pFile uint32, eLock vfsLockS
return uint32(rc)
}
ptr.SetLock(_SHARED_LOCK)
fLock.state = _SHARED_LOCK
return _OK
}
}
@@ -192,23 +200,15 @@ func vfsUnlock(ctx context.Context, mod api.Module, pFile uint32, eLock vfsLockS
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.
// Decrement the shared lock counter.
switch {
case fLock.shared == 1:
if rc := fLock.Release(); rc != _OK {
return uint32(rc)
}
fallthrough
case fLock.shared > 1:
ptr.SetLock(_NO_LOCK)
fLock.shared--
return _OK
default:
panic(assertErr())
ptr.SetLock(_NO_LOCK)
if fLock.shared--; fLock.shared == 0 {
fLock.state = _NO_LOCK
return uint32(fLock.Release())
}
return _OK
}
func vfsCheckReservedLock(ctx context.Context, mod api.Module, pFile, pResOut uint32) uint32 {
@@ -223,15 +223,16 @@ func vfsCheckReservedLock(ctx context.Context, mod api.Module, pFile, pResOut ui
fLock.Lock()
defer fLock.Unlock()
locked, rc := fLock.CheckReserved()
if rc != _OK {
return uint32(IOERR_CHECKRESERVEDLOCK)
if fLock.state >= _RESERVED_LOCK {
memory{mod}.writeUint32(pResOut, 1)
return _OK
}
locked, rc := fLock.CheckReserved()
var res uint32
if locked {
res = 1
}
memory{mod}.writeUint32(pResOut, res)
return _OK
return uint32(rc)
}

View File

@@ -13,10 +13,6 @@ func deleteOnClose(f *os.File) {
}
func (l *vfsFileLocker) GetShared() ExtendedErrorCode {
if l.state != _NO_LOCK {
panic(assertErr())
}
// A PENDING lock is needed before acquiring a SHARED lock.
if err := l.fcntlSetLock(&syscall.Flock_t{
Type: syscall.F_RDLCK,
@@ -27,83 +23,51 @@ func (l *vfsFileLocker) GetShared() ExtendedErrorCode {
}
// Acquire the SHARED lock.
if err := l.fcntlSetLock(&syscall.Flock_t{
rc := l.errorCode(l.fcntlSetLock(&syscall.Flock_t{
Type: syscall.F_RDLCK,
Start: _SHARED_FIRST,
Len: _SHARED_SIZE,
}); err != nil {
return l.errorCode(err, IOERR_LOCK)
}
l.state = _SHARED_LOCK
}), IOERR_LOCK)
// Relese the PENDING lock.
// Drop the temporary PENDING lock.
if err := l.fcntlSetLock(&syscall.Flock_t{
Type: syscall.F_UNLCK,
Start: _PENDING_BYTE,
Len: 1,
}); err != nil {
}); rc == _OK && err != nil {
return IOERR_UNLOCK
}
return _OK
return rc
}
func (l *vfsFileLocker) GetReserved() ExtendedErrorCode {
if l.state != _SHARED_LOCK {
panic(assertErr())
}
// Acquire the RESERVED lock.
if err := l.fcntlSetLock(&syscall.Flock_t{
return l.errorCode(l.fcntlSetLock(&syscall.Flock_t{
Type: syscall.F_WRLCK,
Start: _RESERVED_BYTE,
Len: 1,
}); err != nil {
return l.errorCode(err, IOERR_LOCK)
}
l.state = _RESERVED_LOCK
return _OK
}), IOERR_LOCK)
}
func (l *vfsFileLocker) GetPending() ExtendedErrorCode {
if l.state != _RESERVED_LOCK {
panic(assertErr())
}
// Acquire the PENDING lock.
if err := l.fcntlSetLock(&syscall.Flock_t{
return l.errorCode(l.fcntlSetLock(&syscall.Flock_t{
Type: syscall.F_WRLCK,
Start: _PENDING_BYTE,
Len: 1,
}); err != nil {
return l.errorCode(err, IOERR_LOCK)
}
l.state = _PENDING_LOCK
return _OK
}), IOERR_LOCK)
}
func (l *vfsFileLocker) GetExclusive() ExtendedErrorCode {
if l.state != _SHARED_LOCK && l.state != _PENDING_LOCK {
panic(assertErr())
}
// Acquire the EXCLUSIVE lock.
if err := l.fcntlSetLock(&syscall.Flock_t{
return l.errorCode(l.fcntlSetLock(&syscall.Flock_t{
Type: syscall.F_WRLCK,
Start: _SHARED_FIRST,
Len: _SHARED_SIZE,
}); err != nil {
return l.errorCode(err, IOERR_LOCK)
}
l.state = _EXCLUSIVE_LOCK
return _OK
}), IOERR_LOCK)
}
func (l *vfsFileLocker) Downgrade() ExtendedErrorCode {
if l.state <= _SHARED_LOCK {
panic(assertErr())
}
// Downgrade to a SHARED lock.
if err := l.fcntlSetLock(&syscall.Flock_t{
Type: syscall.F_RDLCK,
@@ -117,7 +81,6 @@ func (l *vfsFileLocker) Downgrade() ExtendedErrorCode {
// BUSY would confuse the upper layer.
return IOERR_RDLOCK
}
l.state = _SHARED_LOCK
// Release the PENDING and RESERVED locks.
if err := l.fcntlSetLock(&syscall.Flock_t{
@@ -131,24 +94,16 @@ func (l *vfsFileLocker) Downgrade() ExtendedErrorCode {
}
func (l *vfsFileLocker) Release() ExtendedErrorCode {
if l.state <= _NO_LOCK {
panic(assertErr())
}
// Release all locks.
if err := l.fcntlSetLock(&syscall.Flock_t{
Type: syscall.F_UNLCK,
}); err != nil {
return IOERR_UNLOCK
}
l.state = _NO_LOCK
return _OK
}
func (l *vfsFileLocker) CheckReserved() (bool, ExtendedErrorCode) {
if l.state >= _RESERVED_LOCK {
return true, _OK
}
// Test the RESERVED lock.
lock := syscall.Flock_t{
Type: syscall.F_RDLCK,
@@ -188,6 +143,9 @@ func (l *vfsFileLocker) fcntlSetLock(lock *syscall.Flock_t) error {
}
func (*vfsFileLocker) errorCode(err error, def ExtendedErrorCode) ExtendedErrorCode {
if err == nil {
return _OK
}
if errno, ok := err.(syscall.Errno); ok {
switch errno {
case syscall.EACCES:

View File

@@ -5,38 +5,29 @@ import "os"
func deleteOnClose(f *os.File) {}
func (l *vfsFileLocker) GetShared() ExtendedErrorCode {
l.state = _SHARED_LOCK
return _OK
}
func (l *vfsFileLocker) GetReserved() ExtendedErrorCode {
l.state = _RESERVED_LOCK
return _OK
}
func (l *vfsFileLocker) GetPending() ExtendedErrorCode {
l.state = _PENDING_LOCK
return _OK
}
func (l *vfsFileLocker) GetExclusive() ExtendedErrorCode {
l.state = _EXCLUSIVE_LOCK
return _OK
}
func (l *vfsFileLocker) Downgrade() ExtendedErrorCode {
l.state = _SHARED_LOCK
return _OK
}
func (l *vfsFileLocker) Release() ExtendedErrorCode {
l.state = _NO_LOCK
return _OK
}
func (l *vfsFileLocker) CheckReserved() (bool, ExtendedErrorCode) {
if l.state >= _RESERVED_LOCK {
return true, _OK
}
return false, _OK
}