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 }