summaryrefslogtreecommitdiff
path: root/vendor/github.com/ncruces/go-sqlite3/vfs/os_unix_lock.go
blob: d04c1f6a0a1831a4f32891c042555a104c029bf0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
//go:build (linux || darwin || freebsd || openbsd || netbsd || dragonfly || illumos || sqlite3_flock) && !sqlite3_nosys

package vfs

import (
	"os"
	"time"

	"golang.org/x/sys/unix"
)

func osGetSharedLock(file *os.File) _ErrorCode {
	// Test the PENDING lock before acquiring a new SHARED lock.
	if lock, _ := osGetLock(file, _PENDING_BYTE, 1); lock == unix.F_WRLCK {
		return _BUSY
	}
	// Acquire the SHARED lock.
	return osReadLock(file, _SHARED_FIRST, _SHARED_SIZE, 0)
}

func osGetReservedLock(file *os.File) _ErrorCode {
	// Acquire the RESERVED lock.
	return osWriteLock(file, _RESERVED_BYTE, 1, 0)
}

func osGetPendingLock(file *os.File, block bool) _ErrorCode {
	var timeout time.Duration
	if block {
		timeout = -1
	}
	// Acquire the PENDING lock.
	return osWriteLock(file, _PENDING_BYTE, 1, timeout)
}

func osGetExclusiveLock(file *os.File, wait bool) _ErrorCode {
	var timeout time.Duration
	if wait {
		timeout = time.Millisecond
	}
	// Acquire the EXCLUSIVE lock.
	return osWriteLock(file, _SHARED_FIRST, _SHARED_SIZE, timeout)
}

func osDowngradeLock(file *os.File, state LockLevel) _ErrorCode {
	if state >= LOCK_EXCLUSIVE {
		// Downgrade to a SHARED lock.
		if rc := osReadLock(file, _SHARED_FIRST, _SHARED_SIZE, 0); rc != _OK {
			// In theory, the downgrade to a SHARED cannot fail because another
			// process is holding an incompatible lock. If it does, this
			// indicates that the other process is not following the locking
			// protocol. If this happens, return _IOERR_RDLOCK. Returning
			// BUSY would confuse the upper layer.
			return _IOERR_RDLOCK
		}
	}
	// Release the PENDING and RESERVED locks.
	return osUnlock(file, _PENDING_BYTE, 2)
}

func osReleaseLock(file *os.File, _ LockLevel) _ErrorCode {
	// Release all locks.
	return osUnlock(file, 0, 0)
}

func osCheckReservedLock(file *os.File) (bool, _ErrorCode) {
	// Test the RESERVED lock.
	lock, rc := osGetLock(file, _RESERVED_BYTE, 1)
	return lock == unix.F_WRLCK, rc
}

func osGetLock(file *os.File, start, len int64) (int16, _ErrorCode) {
	lock := unix.Flock_t{
		Type:  unix.F_WRLCK,
		Start: start,
		Len:   len,
	}
	if unix.FcntlFlock(file.Fd(), unix.F_GETLK, &lock) != nil {
		return 0, _IOERR_CHECKRESERVEDLOCK
	}
	return lock.Type, _OK
}

func osLockErrorCode(err error, def _ErrorCode) _ErrorCode {
	if err == nil {
		return _OK
	}
	if errno, ok := err.(unix.Errno); ok {
		switch errno {
		case
			unix.EACCES,
			unix.EAGAIN,
			unix.EBUSY,
			unix.EINTR,
			unix.ENOLCK,
			unix.EDEADLK,
			unix.ETIMEDOUT:
			return _BUSY
		case unix.EPERM:
			return _PERM
		}
		if errno == unix.EWOULDBLOCK && unix.EWOULDBLOCK != unix.EAGAIN {
			return _BUSY
		}
	}
	return def
}