blob: 05b9af8c39147d71bc9a15b25b79540bd8bd2020 [file] [log] [blame]
// package filelocker provides a primitive that lets a process
// lock access to a file.
package filelocker
import (
"os"
"syscall"
"v.io/x/lib/vlog"
)
// Unlocker is the interface to unlock a locked file.
type Unlocker interface {
// Unlock unlocks the file.
Unlock()
}
// Lock locks the provided file.
//
// If the file is already locked then the calling goroutine
// blocks until the lock can be grabbed.
//
// The file must exist otherwise an error is returned.
func Lock(filepath string) (Unlocker, error) {
file, err := os.Open(filepath)
if err != nil {
return nil, err
}
if err = syscall.Flock(int(file.Fd()), syscall.LOCK_EX); err != nil {
return nil, err
}
return &unlocker{file}, nil
}
// TryLock tries to grab a lock on the provided file and
// returns an error if it fails. This function is non-blocking.
//
// The file must exist otherwise an error is returned.
func TryLock(filepath string) (Unlocker, error) {
file, err := os.Open(filepath)
if err != nil {
return nil, err
}
if err = syscall.Flock(int(file.Fd()), syscall.LOCK_EX|syscall.LOCK_NB); err != nil {
return nil, err
}
return &unlocker{file}, nil
}
// unlocker implements Unlocker.
type unlocker struct {
file *os.File
}
func (u *unlocker) Unlock() {
if err := u.file.Close(); err != nil {
vlog.Errorf("failed to unlock file %q: %v", u.file.Name(), err)
}
}