1//go:build unix
2
3package dotlk
4
5import (
6 "errors"
7 "io/fs"
8 "os"
9 "strconv"
10
11 "golang.org/x/sys/unix"
12)
13
14// TryLock returns nil if it acquired the lock,
15// fs.ErrExist if another process has the lock.
16func TryLock(name string) error {
17 for retry := true; retry; retry = false {
18 f, err := os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666)
19 if err == nil {
20 f.WriteString(strconv.Itoa(os.Getpid()))
21 f.Close()
22 return nil
23 }
24 if !errors.Is(err, fs.ErrExist) {
25 return err
26 }
27 if !removeStale(name) {
28 break
29 }
30 }
31 return fs.ErrExist
32}
33
34func removeStale(name string) bool {
35 buf, err := os.ReadFile(name)
36 if err != nil {
37 return errors.Is(err, fs.ErrNotExist)
38 }
39
40 pid, err := strconv.Atoi(string(buf))
41 if err != nil {
42 return false
43 }
44 if unix.Kill(pid, 0) == nil {
45 return false
46 }
47
48 err = os.Remove(name)
49 return err == nil || errors.Is(err, fs.ErrNotExist)
50}