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"
|
"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()
|
||||||
|
@ -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) {
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
@ -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}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user