go: time out flock differently

Use channels and timers to time out a blocking flock, rather than
non-blocking + polling.

Change-Id: Idd7d5d035a5ccf1c609bbd79575ded1e896a3f07
This commit is contained in:
Michael Barton 2016-06-27 17:58:49 +00:00
parent a331f77253
commit ab08119d2f
4 changed files with 48 additions and 16 deletions

View File

@ -29,6 +29,7 @@ import (
"strconv" "strconv"
"strings" "strings"
"syscall" "syscall"
"time"
"github.com/shirou/gopsutil/disk" "github.com/shirou/gopsutil/disk"
"github.com/shirou/gopsutil/load" "github.com/shirou/gopsutil/load"
@ -39,7 +40,7 @@ import (
func DumpReconCache(reconCachePath string, source string, cacheData map[string]interface{}) error { func DumpReconCache(reconCachePath string, source string, cacheData map[string]interface{}) error {
reconFile := filepath.Join(reconCachePath, source+".recon") reconFile := filepath.Join(reconCachePath, source+".recon")
if lock, err := LockParent(reconFile, 5); err != nil { if lock, err := LockPath(filepath.Dir(reconFile), 5*time.Second); err != nil {
return err return err
} else { } else {
defer lock.Close() defer lock.Close()

View File

@ -84,32 +84,39 @@ func WriteFileAtomic(filename string, data []byte, perm os.FileMode) error {
return nil return nil
} }
func LockPath(directory string, timeout int) (*os.File, error) { // LockPath locks a directory with a timeout.
sleepTime := 5 func LockPath(directory string, timeout time.Duration) (*os.File, error) {
lockfile := filepath.Join(directory, ".lock") lockfile := filepath.Join(directory, ".lock")
file, err := os.OpenFile(lockfile, os.O_RDWR|os.O_CREATE, 0660) file, err := os.OpenFile(lockfile, os.O_RDWR|os.O_CREATE, 0660)
if err != nil { if err != nil {
if os.IsNotExist(err) && os.MkdirAll(directory, 0755) == nil { if os.IsNotExist(err) && os.MkdirAll(directory, 0755) == nil {
file, err = os.OpenFile(lockfile, os.O_RDWR|os.O_CREATE, 0660) file, err = os.OpenFile(lockfile, os.O_RDWR|os.O_CREATE, 0660)
} }
if file == nil { if err != nil {
return nil, errors.New(fmt.Sprintf("Unable to open file ccc. ( %s )", err.Error())) return nil, errors.New(fmt.Sprintf("Unable to open lock file (%v)", err))
} }
} }
for stop := time.Now().Add(time.Duration(timeout) * time.Second); time.Now().Before(stop); { success := make(chan error)
err = syscall.Flock(int(file.Fd()), syscall.LOCK_EX|syscall.LOCK_NB) cancel := make(chan struct{})
defer close(cancel)
timer := time.NewTimer(timeout)
defer timer.Stop()
go func(fd int) {
select {
case success <- syscall.Flock(fd, syscall.LOCK_EX):
case <-cancel:
}
}(int(file.Fd()))
select {
case err = <-success:
if err == nil { if err == nil {
return file, nil return file, nil
} }
time.Sleep(time.Millisecond * time.Duration(sleepTime)) case <-timer.C:
sleepTime += 5 err = errors.New("Flock timed out")
} }
file.Close() file.Close()
return nil, errors.New("Timed out") return nil, err
}
func LockParent(file string, timeout int) (*os.File, error) {
return LockPath(filepath.Dir(file), timeout)
} }
func IsMount(dir string) (bool, error) { func IsMount(dir string) (bool, error) {

View File

@ -288,3 +288,27 @@ func TestGetHashPrefixAndSuffix(t *testing.T) {
t.Error("Error prefix and suffix not being set") t.Error("Error prefix and suffix not being set")
} }
} }
func TestLockPath(t *testing.T) {
tempDir, err := ioutil.TempDir("", "")
defer os.RemoveAll(tempDir)
require.Nil(t, err)
c := make(chan bool)
ended := make(chan struct{})
defer close(ended)
go func() {
f, err := LockPath(tempDir, time.Millisecond)
c <- true
require.Nil(t, err)
require.NotNil(t, f)
defer f.Close()
select {
case <-time.After(time.Second):
case <-ended:
}
}()
<-c
f, err := LockPath(tempDir, time.Millisecond)
require.Nil(t, f)
require.NotNil(t, err)
}

View File

@ -150,7 +150,7 @@ func InvalidateHash(hashDir string) error {
suffDir := filepath.Dir(hashDir) suffDir := filepath.Dir(hashDir)
partitionDir := filepath.Dir(suffDir) partitionDir := filepath.Dir(suffDir)
if partitionLock, err := hummingbird.LockPath(partitionDir, 10); err != nil { if partitionLock, err := hummingbird.LockPath(partitionDir, 10*time.Second); err != nil {
return err return err
} else { } else {
defer partitionLock.Close() defer partitionLock.Close()
@ -322,7 +322,7 @@ func GetHashes(driveRoot string, device string, partition string, recalculate []
} }
} }
if modified { if modified {
partitionLock, err := hummingbird.LockPath(partitionDir, 10) partitionLock, err := hummingbird.LockPath(partitionDir, 10*time.Second)
defer partitionLock.Close() defer partitionLock.Close()
if err != nil { if err != nil {
return nil, &hummingbird.BackendError{Err: err, Code: hummingbird.LockPathError} return nil, &hummingbird.BackendError{Err: err, Code: hummingbird.LockPathError}