dotlk_unix.go

 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}