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