github.com/ncruces/go-sqlite3@v0.15.1-0.20240520133447-53eef1510ff0/vfs/os_unix_lock.go (about)

     1  //go:build (linux || darwin || freebsd || openbsd || netbsd || dragonfly || illumos || sqlite3_flock) && !sqlite3_nosys
     2  
     3  package vfs
     4  
     5  import (
     6  	"os"
     7  	"time"
     8  
     9  	"golang.org/x/sys/unix"
    10  )
    11  
    12  func osGetSharedLock(file *os.File) _ErrorCode {
    13  	// Test the PENDING lock before acquiring a new SHARED lock.
    14  	if lock, _ := osGetLock(file, _PENDING_BYTE, 1); lock == unix.F_WRLCK {
    15  		return _BUSY
    16  	}
    17  	// Acquire the SHARED lock.
    18  	return osReadLock(file, _SHARED_FIRST, _SHARED_SIZE, 0)
    19  }
    20  
    21  func osGetReservedLock(file *os.File) _ErrorCode {
    22  	// Acquire the RESERVED lock.
    23  	return osWriteLock(file, _RESERVED_BYTE, 1, 0)
    24  }
    25  
    26  func osGetPendingLock(file *os.File, block bool) _ErrorCode {
    27  	var timeout time.Duration
    28  	if block {
    29  		timeout = -1
    30  	}
    31  	// Acquire the PENDING lock.
    32  	return osWriteLock(file, _PENDING_BYTE, 1, timeout)
    33  }
    34  
    35  func osGetExclusiveLock(file *os.File, wait bool) _ErrorCode {
    36  	var timeout time.Duration
    37  	if wait {
    38  		timeout = time.Millisecond
    39  	}
    40  	// Acquire the EXCLUSIVE lock.
    41  	return osWriteLock(file, _SHARED_FIRST, _SHARED_SIZE, timeout)
    42  }
    43  
    44  func osDowngradeLock(file *os.File, state LockLevel) _ErrorCode {
    45  	if state >= LOCK_EXCLUSIVE {
    46  		// Downgrade to a SHARED lock.
    47  		if rc := osReadLock(file, _SHARED_FIRST, _SHARED_SIZE, 0); rc != _OK {
    48  			// In theory, the downgrade to a SHARED cannot fail because another
    49  			// process is holding an incompatible lock. If it does, this
    50  			// indicates that the other process is not following the locking
    51  			// protocol. If this happens, return _IOERR_RDLOCK. Returning
    52  			// BUSY would confuse the upper layer.
    53  			return _IOERR_RDLOCK
    54  		}
    55  	}
    56  	// Release the PENDING and RESERVED locks.
    57  	return osUnlock(file, _PENDING_BYTE, 2)
    58  }
    59  
    60  func osReleaseLock(file *os.File, _ LockLevel) _ErrorCode {
    61  	// Release all locks.
    62  	return osUnlock(file, 0, 0)
    63  }
    64  
    65  func osCheckReservedLock(file *os.File) (bool, _ErrorCode) {
    66  	// Test the RESERVED lock.
    67  	lock, rc := osGetLock(file, _RESERVED_BYTE, 1)
    68  	return lock == unix.F_WRLCK, rc
    69  }
    70  
    71  func osGetLock(file *os.File, start, len int64) (int16, _ErrorCode) {
    72  	lock := unix.Flock_t{
    73  		Type:  unix.F_WRLCK,
    74  		Start: start,
    75  		Len:   len,
    76  	}
    77  	if unix.FcntlFlock(file.Fd(), unix.F_GETLK, &lock) != nil {
    78  		return 0, _IOERR_CHECKRESERVEDLOCK
    79  	}
    80  	return lock.Type, _OK
    81  }
    82  
    83  func osLockErrorCode(err error, def _ErrorCode) _ErrorCode {
    84  	if err == nil {
    85  		return _OK
    86  	}
    87  	if errno, ok := err.(unix.Errno); ok {
    88  		switch errno {
    89  		case
    90  			unix.EACCES,
    91  			unix.EAGAIN,
    92  			unix.EBUSY,
    93  			unix.EINTR,
    94  			unix.ENOLCK,
    95  			unix.EDEADLK,
    96  			unix.ETIMEDOUT:
    97  			return _BUSY
    98  		case unix.EPERM:
    99  			return _PERM
   100  		}
   101  		if errno == unix.EWOULDBLOCK && unix.EWOULDBLOCK != unix.EAGAIN {
   102  			return _BUSY
   103  		}
   104  	}
   105  	return def
   106  }