-
Notifications
You must be signed in to change notification settings - Fork 8
Expand file tree
/
Copy pathlock.go
More file actions
110 lines (95 loc) · 2.37 KB
/
lock.go
File metadata and controls
110 lines (95 loc) · 2.37 KB
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
107
108
109
110
// Unless explicitly stated otherwise all files in this repository are licensed
// under the MIT License.
// This product includes software developed at Guance Cloud (https://www.guance.com/).
// Copyright 2021-present Guance, Inc.
package diskcache
import (
"fmt"
"os"
"path/filepath"
"runtime"
"strconv"
"sync"
"syscall"
)
type flock struct {
file string
mtx *sync.Mutex
}
func newFlock(path string) *flock {
return &flock{
file: filepath.Clean(filepath.Join(path, ".lock")),
mtx: &sync.Mutex{},
}
}
func (l *flock) lock() error {
l.mtx.Lock()
defer l.mtx.Unlock()
curPid := os.Getpid()
if _, err := os.Stat(l.file); err != nil {
goto write // file not exist
} else {
x, err := os.ReadFile(l.file)
if err != nil {
return WrapFileOperationError(OpRead, err, "", l.file).
WithDetails("failed_to_read_lock_file")
}
if len(x) == 0 {
goto write
}
pidInFile, err := strconv.Atoi(string(x))
if err != nil {
return NewCacheError(OpLock, err,
fmt.Sprintf("failed_to_parse_pid_from_lock_file: content=%q", string(x))).
WithFile(l.file)
} else {
switch pidInFile {
case -1: // unlocked
goto write
case curPid:
return NewCacheError(OpLock, fmt.Errorf("already_locked_by_current_process"), "").
WithFile(l.file).WithDetails(fmt.Sprintf("current_pid=%d", curPid))
default: // other pid, may terminated
if pidAlive(pidInFile) {
return WrapLockError(fmt.Errorf("process_already_has_lock"), "", pidInFile).
WithFile(l.file)
}
}
}
}
write:
if err := os.WriteFile(l.file, []byte(strconv.Itoa(curPid)), 0o600); err != nil {
return WrapFileOperationError(OpWrite, err, "", l.file).
WithDetails(fmt.Sprintf("failed_to_write_pid_to_lock_file: pid=%d", curPid))
}
return nil
}
func (l *flock) unlock() error {
l.mtx.Lock()
defer l.mtx.Unlock()
if err := os.WriteFile(l.file, []byte(strconv.Itoa(-1)), 0o600); err != nil {
return WrapFileOperationError(OpWrite, err, "", l.file).
WithDetails("failed_to_write_unlock_marker")
}
return nil
}
func pidAlive(pid int) bool {
p, err := os.FindProcess(pid)
if err != nil {
return false
}
// Signal not available on windows.
if runtime.GOOS == "windows" {
return true
}
if err := p.Signal(syscall.Signal(0)); err != nil {
switch err.Error() {
case "operation not permitted":
return true
default:
return false
}
} else {
return true
}
}