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:
parent
a331f77253
commit
ab08119d2f
@ -29,6 +29,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/shirou/gopsutil/disk"
|
||||
"github.com/shirou/gopsutil/load"
|
||||
@ -39,7 +40,7 @@ import (
|
||||
func DumpReconCache(reconCachePath string, source string, cacheData map[string]interface{}) error {
|
||||
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
|
||||
} else {
|
||||
defer lock.Close()
|
||||
|
@ -84,32 +84,39 @@ func WriteFileAtomic(filename string, data []byte, perm os.FileMode) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func LockPath(directory string, timeout int) (*os.File, error) {
|
||||
sleepTime := 5
|
||||
// LockPath locks a directory with a timeout.
|
||||
func LockPath(directory string, timeout time.Duration) (*os.File, error) {
|
||||
lockfile := filepath.Join(directory, ".lock")
|
||||
file, err := os.OpenFile(lockfile, os.O_RDWR|os.O_CREATE, 0660)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) && os.MkdirAll(directory, 0755) == nil {
|
||||
file, err = os.OpenFile(lockfile, os.O_RDWR|os.O_CREATE, 0660)
|
||||
}
|
||||
if file == nil {
|
||||
return nil, errors.New(fmt.Sprintf("Unable to open file ccc. ( %s )", err.Error()))
|
||||
if err != nil {
|
||||
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); {
|
||||
err = syscall.Flock(int(file.Fd()), syscall.LOCK_EX|syscall.LOCK_NB)
|
||||
success := make(chan error)
|
||||
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 {
|
||||
return file, nil
|
||||
}
|
||||
time.Sleep(time.Millisecond * time.Duration(sleepTime))
|
||||
sleepTime += 5
|
||||
case <-timer.C:
|
||||
err = errors.New("Flock timed out")
|
||||
}
|
||||
file.Close()
|
||||
return nil, errors.New("Timed out")
|
||||
}
|
||||
|
||||
func LockParent(file string, timeout int) (*os.File, error) {
|
||||
return LockPath(filepath.Dir(file), timeout)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func IsMount(dir string) (bool, error) {
|
||||
|
@ -288,3 +288,27 @@ func TestGetHashPrefixAndSuffix(t *testing.T) {
|
||||
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)
|
||||
}
|
||||
|
@ -150,7 +150,7 @@ func InvalidateHash(hashDir string) error {
|
||||
suffDir := filepath.Dir(hashDir)
|
||||
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
|
||||
} else {
|
||||
defer partitionLock.Close()
|
||||
@ -322,7 +322,7 @@ func GetHashes(driveRoot string, device string, partition string, recalculate []
|
||||
}
|
||||
}
|
||||
if modified {
|
||||
partitionLock, err := hummingbird.LockPath(partitionDir, 10)
|
||||
partitionLock, err := hummingbird.LockPath(partitionDir, 10*time.Second)
|
||||
defer partitionLock.Close()
|
||||
if err != nil {
|
||||
return nil, &hummingbird.BackendError{Err: err, Code: hummingbird.LockPathError}
|
||||
|
Loading…
Reference in New Issue
Block a user