1// +build !windows,!plan9
2
3// TODO: Figure out what to do for Plan 9. It doesn't support FcntlFlock at all,
4// but we might be able to ~emulate it with os.O_EXCL, but that wouldn't allow
5// us to automatically release locks if a process dies.
6
7package locking
8
9import (
10	"os"
11
12	"golang.org/x/sys/unix"
13
14	"github.com/pkg/errors"
15)
16
17// Lock attempts to acquire the file lock.
18func (l *Locker) Lock(block bool) error {
19	// Verify that we don't already hold the lock.
20	if l.held {
21		return errors.New("lock already held")
22	}
23
24	// Set up the lock specification.
25	lockSpec := unix.Flock_t{
26		Type:   unix.F_WRLCK,
27		Whence: int16(os.SEEK_SET),
28		Start:  0,
29		Len:    0,
30	}
31
32	// Set up the blocking specification.
33	operation := unix.F_SETLK
34	if block {
35		operation = unix.F_SETLKW
36	}
37
38	// Attempt to perform locking.
39	err := unix.FcntlFlock(l.file.Fd(), operation, &lockSpec)
40
41	// Check for success and update the internal state.
42	if err == nil {
43		l.held = true
44	}
45
46	// Done.
47	return err
48}
49
50// Unlock releases the file lock.
51func (l *Locker) Unlock() error {
52	// Verify that we hold the lock.
53	if !l.held {
54		return errors.New("lock not held")
55	}
56
57	// Set up the unlock specification.
58	unlockSpec := unix.Flock_t{
59		Type:   unix.F_UNLCK,
60		Whence: int16(os.SEEK_SET),
61		Start:  0,
62		Len:    0,
63	}
64
65	// Attempt to perform unlocking.
66	err := unix.FcntlFlock(l.file.Fd(), unix.F_SETLK, &unlockSpec)
67
68	// Check for success and update the internal state.
69	if err == nil {
70		l.held = false
71	}
72
73	// Done.
74	return err
75}
76