github.com/ncruces/go-sqlite3@v0.15.1-0.20240520133447-53eef1510ff0/vfs/os_windows.go (about) 1 //go:build !sqlite3_nosys 2 3 package vfs 4 5 import ( 6 "math/rand" 7 "os" 8 "time" 9 10 "golang.org/x/sys/windows" 11 ) 12 13 func osGetSharedLock(file *os.File) _ErrorCode { 14 // Acquire the PENDING lock temporarily before acquiring a new SHARED lock. 15 rc := osReadLock(file, _PENDING_BYTE, 1, 0) 16 if rc == _OK { 17 // Acquire the SHARED lock. 18 rc = osReadLock(file, _SHARED_FIRST, _SHARED_SIZE, 0) 19 20 // Release the PENDING lock. 21 osUnlock(file, _PENDING_BYTE, 1) 22 } 23 return rc 24 } 25 26 func osGetReservedLock(file *os.File) _ErrorCode { 27 // Acquire the RESERVED lock. 28 return osWriteLock(file, _RESERVED_BYTE, 1, 0) 29 } 30 31 func osGetPendingLock(file *os.File, block bool) _ErrorCode { 32 var timeout time.Duration 33 if block { 34 timeout = -1 35 } 36 37 // Acquire the PENDING lock. 38 return osWriteLock(file, _PENDING_BYTE, 1, timeout) 39 } 40 41 func osGetExclusiveLock(file *os.File, wait bool) _ErrorCode { 42 var timeout time.Duration 43 if wait { 44 timeout = time.Millisecond 45 } 46 47 // Release the SHARED lock. 48 osUnlock(file, _SHARED_FIRST, _SHARED_SIZE) 49 50 // Acquire the EXCLUSIVE lock. 51 rc := osWriteLock(file, _SHARED_FIRST, _SHARED_SIZE, timeout) 52 53 if rc != _OK { 54 // Reacquire the SHARED lock. 55 osReadLock(file, _SHARED_FIRST, _SHARED_SIZE, 0) 56 } 57 return rc 58 } 59 60 func osDowngradeLock(file *os.File, state LockLevel) _ErrorCode { 61 if state >= LOCK_EXCLUSIVE { 62 // Release the EXCLUSIVE lock. 63 osUnlock(file, _SHARED_FIRST, _SHARED_SIZE) 64 65 // Reacquire the SHARED lock. 66 if rc := osReadLock(file, _SHARED_FIRST, _SHARED_SIZE, 0); rc != _OK { 67 // This should never happen. 68 // We should always be able to reacquire the read lock. 69 return _IOERR_RDLOCK 70 } 71 } 72 73 // Release the PENDING and RESERVED locks. 74 if state >= LOCK_RESERVED { 75 osUnlock(file, _RESERVED_BYTE, 1) 76 } 77 if state >= LOCK_PENDING { 78 osUnlock(file, _PENDING_BYTE, 1) 79 } 80 return _OK 81 } 82 83 func osReleaseLock(file *os.File, state LockLevel) _ErrorCode { 84 // Release all locks. 85 if state >= LOCK_RESERVED { 86 osUnlock(file, _RESERVED_BYTE, 1) 87 } 88 if state >= LOCK_SHARED { 89 osUnlock(file, _SHARED_FIRST, _SHARED_SIZE) 90 } 91 if state >= LOCK_PENDING { 92 osUnlock(file, _PENDING_BYTE, 1) 93 } 94 return _OK 95 } 96 97 func osCheckReservedLock(file *os.File) (bool, _ErrorCode) { 98 // Test the RESERVED lock. 99 rc := osLock(file, 0, _RESERVED_BYTE, 1, 0, _IOERR_CHECKRESERVEDLOCK) 100 if rc == _BUSY { 101 return true, _OK 102 } 103 if rc == _OK { 104 // Release the RESERVED lock. 105 osUnlock(file, _RESERVED_BYTE, 1) 106 } 107 return false, rc 108 } 109 110 func osUnlock(file *os.File, start, len uint32) _ErrorCode { 111 err := windows.UnlockFileEx(windows.Handle(file.Fd()), 112 0, len, 0, &windows.Overlapped{Offset: start}) 113 if err == windows.ERROR_NOT_LOCKED { 114 return _OK 115 } 116 if err != nil { 117 return _IOERR_UNLOCK 118 } 119 return _OK 120 } 121 122 func osLock(file *os.File, flags, start, len uint32, timeout time.Duration, def _ErrorCode) _ErrorCode { 123 var err error 124 switch { 125 case timeout == 0: 126 err = osLockEx(file, flags|windows.LOCKFILE_FAIL_IMMEDIATELY, start, len) 127 case timeout < 0: 128 err = osLockEx(file, flags, start, len) 129 default: 130 before := time.Now() 131 for { 132 err = osLockEx(file, flags|windows.LOCKFILE_FAIL_IMMEDIATELY, start, len) 133 if errno, _ := err.(windows.Errno); errno != windows.ERROR_LOCK_VIOLATION { 134 break 135 } 136 if timeout < time.Since(before) { 137 break 138 } 139 osSleep(time.Duration(rand.Int63n(int64(time.Millisecond)))) 140 } 141 } 142 return osLockErrorCode(err, def) 143 } 144 145 func osLockEx(file *os.File, flags, start, len uint32) error { 146 return windows.LockFileEx(windows.Handle(file.Fd()), flags, 147 0, len, 0, &windows.Overlapped{Offset: start}) 148 } 149 150 func osReadLock(file *os.File, start, len uint32, timeout time.Duration) _ErrorCode { 151 return osLock(file, 0, start, len, timeout, _IOERR_RDLOCK) 152 } 153 154 func osWriteLock(file *os.File, start, len uint32, timeout time.Duration) _ErrorCode { 155 return osLock(file, windows.LOCKFILE_EXCLUSIVE_LOCK, start, len, timeout, _IOERR_LOCK) 156 } 157 158 func osLockErrorCode(err error, def _ErrorCode) _ErrorCode { 159 if err == nil { 160 return _OK 161 } 162 if errno, ok := err.(windows.Errno); ok { 163 // https://devblogs.microsoft.com/oldnewthing/20140905-00/?p=63 164 switch errno { 165 case 166 windows.ERROR_LOCK_VIOLATION, 167 windows.ERROR_IO_PENDING, 168 windows.ERROR_OPERATION_ABORTED: 169 return _BUSY 170 } 171 } 172 return def 173 } 174 175 func osSleep(d time.Duration) { 176 if d > 0 { 177 period := max(1, d/(5*time.Millisecond)) 178 if period < 16 { 179 windows.TimeBeginPeriod(uint32(period)) 180 } 181 time.Sleep(d) 182 if period < 16 { 183 windows.TimeEndPeriod(uint32(period)) 184 } 185 } 186 }