github.com/ncruces/go-sqlite3@v0.15.1-0.20240520133447-53eef1510ff0/vfs/lock.go (about) 1 //go:build (linux || darwin || windows || freebsd || openbsd || netbsd || dragonfly || illumos || sqlite3_flock) && !sqlite3_nosys 2 3 package vfs 4 5 import "github.com/ncruces/go-sqlite3/internal/util" 6 7 // SupportsFileLocking is false on platforms that do not support file locking. 8 // To open a database file on those platforms, 9 // you need to use the [nolock] or [immutable] URI parameters. 10 // 11 // [nolock]: https://sqlite.org/uri.html#urinolock 12 // [immutable]: https://sqlite.org/uri.html#uriimmutable 13 const SupportsFileLocking = true 14 15 const ( 16 _PENDING_BYTE = 0x40000000 17 _RESERVED_BYTE = (_PENDING_BYTE + 1) 18 _SHARED_FIRST = (_PENDING_BYTE + 2) 19 _SHARED_SIZE = 510 20 ) 21 22 func (f *vfsFile) Lock(lock LockLevel) error { 23 // Argument check. SQLite never explicitly requests a pending lock. 24 if lock != LOCK_SHARED && lock != LOCK_RESERVED && lock != LOCK_EXCLUSIVE { 25 panic(util.AssertErr()) 26 } 27 28 switch { 29 case f.lock < LOCK_NONE || f.lock > LOCK_EXCLUSIVE: 30 // Connection state check. 31 panic(util.AssertErr()) 32 case f.lock == LOCK_NONE && lock > LOCK_SHARED: 33 // We never move from unlocked to anything higher than a shared lock. 34 panic(util.AssertErr()) 35 case f.lock != LOCK_SHARED && lock == LOCK_RESERVED: 36 // A shared lock is always held when a reserved lock is requested. 37 panic(util.AssertErr()) 38 } 39 40 // If we already have an equal or more restrictive lock, do nothing. 41 if f.lock >= lock { 42 return nil 43 } 44 45 // Do not allow any kind of write-lock on a read-only database. 46 if f.readOnly && lock >= LOCK_RESERVED { 47 return _IOERR_LOCK 48 } 49 50 switch lock { 51 case LOCK_SHARED: 52 // Must be unlocked to get SHARED. 53 if f.lock != LOCK_NONE { 54 panic(util.AssertErr()) 55 } 56 if rc := osGetSharedLock(f.File); rc != _OK { 57 return rc 58 } 59 f.lock = LOCK_SHARED 60 return nil 61 62 case LOCK_RESERVED: 63 // Must be SHARED to get RESERVED. 64 if f.lock != LOCK_SHARED { 65 panic(util.AssertErr()) 66 } 67 if rc := osGetReservedLock(f.File); rc != _OK { 68 return rc 69 } 70 f.lock = LOCK_RESERVED 71 return nil 72 73 case LOCK_EXCLUSIVE: 74 // Must be SHARED, RESERVED or PENDING to get EXCLUSIVE. 75 if f.lock <= LOCK_NONE || f.lock >= LOCK_EXCLUSIVE { 76 panic(util.AssertErr()) 77 } 78 reserved := f.lock == LOCK_RESERVED 79 // A PENDING lock is needed before acquiring an EXCLUSIVE lock. 80 if f.lock < LOCK_PENDING { 81 // If we're already RESERVED, we can block indefinitely, 82 // since only new readers may briefly hold the PENDING lock. 83 if rc := osGetPendingLock(f.File, reserved /* block */); rc != _OK { 84 return rc 85 } 86 f.lock = LOCK_PENDING 87 } 88 // We already have PENDING, so we're just waiting for readers to leave. 89 // If we were RESERVED, we can wait for a little while, before invoking 90 // the busy handler; we will only do this once. 91 if rc := osGetExclusiveLock(f.File, reserved /* wait */); rc != _OK { 92 return rc 93 } 94 f.lock = LOCK_EXCLUSIVE 95 return nil 96 97 default: 98 panic(util.AssertErr()) 99 } 100 } 101 102 func (f *vfsFile) Unlock(lock LockLevel) error { 103 // Argument check. 104 if lock != LOCK_NONE && lock != LOCK_SHARED { 105 panic(util.AssertErr()) 106 } 107 108 // Connection state check. 109 if f.lock < LOCK_NONE || f.lock > LOCK_EXCLUSIVE { 110 panic(util.AssertErr()) 111 } 112 113 // If we don't have a more restrictive lock, do nothing. 114 if f.lock <= lock { 115 return nil 116 } 117 118 switch lock { 119 case LOCK_SHARED: 120 rc := osDowngradeLock(f.File, f.lock) 121 f.lock = LOCK_SHARED 122 return rc 123 124 case LOCK_NONE: 125 rc := osReleaseLock(f.File, f.lock) 126 f.lock = LOCK_NONE 127 return rc 128 129 default: 130 panic(util.AssertErr()) 131 } 132 } 133 134 func (f *vfsFile) CheckReservedLock() (bool, error) { 135 // Connection state check. 136 if f.lock < LOCK_NONE || f.lock > LOCK_EXCLUSIVE { 137 panic(util.AssertErr()) 138 } 139 140 if f.lock >= LOCK_RESERVED { 141 return true, nil 142 } 143 return osCheckReservedLock(f.File) 144 }