Skip to content

Commit

Permalink
add support for non-blocking Flock()
Browse files Browse the repository at this point in the history
  • Loading branch information
mvo5 committed May 23, 2017
1 parent 7d221df commit bc5a466
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 7 deletions.
2 changes: 1 addition & 1 deletion cmd/snap-update-ns/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ func run() error {
return fmt.Errorf("cannot open lock file for mount namespace of snap %q: %s", snapName, err)
}
defer lock.Close()
if err := lock.Lock(); err != nil {
if err := lock.Lock(0); err != nil {
return fmt.Errorf("cannot lock mount namespace of snap %q: %s", snapName, err)
}

Expand Down
13 changes: 11 additions & 2 deletions osutil/flock.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ type FLock struct {
fname string
}

const (
_ = iota
FLockNonBlocking
)

// OpenLock creates and opens a lock file associated with a particular snap.
func OpenLock(fname string) (*FLock, error) {
mode := syscall.O_RDWR | syscall.O_CREAT | syscall.O_NOFOLLOW | syscall.O_CLOEXEC
Expand All @@ -52,8 +57,12 @@ func (l *FLock) Close() error {
}

// Lock acquires an exclusive lock.
func (l *FLock) Lock() error {
return syscall.Flock(int(l.file.Fd()), syscall.LOCK_EX)
func (l *FLock) Lock(flags int) error {
flockFlags := syscall.LOCK_EX
if flags&FLockNonBlocking != 0 {
flockFlags |= syscall.LOCK_NB
}
return syscall.Flock(int(l.file.Fd()), flockFlags)
}

// Unlock releases an acquired lock.
Expand Down
35 changes: 31 additions & 4 deletions osutil/flock_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"os"
"os/exec"
"path/filepath"
"time"

. "gopkg.in/check.v1"

Expand Down Expand Up @@ -60,7 +61,7 @@ func (s *flockSuite) TestLockUnlockWorks(c *C) {
c.Assert(cmd.Run(), IsNil)

// Lock the lock.
c.Assert(lock.Lock(), IsNil)
c.Assert(lock.Lock(0), IsNil)

// Run a flock command in another process, it should fail with the distinct
// error code because we hold the lock already and we asked it not to block.
Expand All @@ -84,8 +85,8 @@ func (s *flockSuite) TestLockLocked(c *C) {
defer lock.Close()

// NOTE: technically this replaces the lock type but we only use LOCK_EX.
c.Assert(lock.Lock(), IsNil)
c.Assert(lock.Lock(), IsNil)
c.Assert(lock.Lock(0), IsNil)
c.Assert(lock.Lock(0), IsNil)
}

// Test that unlocking an unlocked lock does nothing.
Expand All @@ -103,6 +104,32 @@ func (s *flockSuite) TestUsingClosedLock(c *C) {
c.Assert(err, IsNil)
lock.Close()

c.Assert(lock.Lock(), ErrorMatches, "bad file descriptor")
c.Assert(lock.Lock(0), ErrorMatches, "bad file descriptor")
c.Assert(lock.Unlock(), ErrorMatches, "bad file descriptor")
}

// Test that non-blocking
func (s *flockSuite) TestLockUnlockNonblockingWorks(c *C) {
if os.Getenv("TRAVIS_BUILD_NUMBER") != "" {
c.Skip("Cannot use this under travis")
return
}

lockPath := filepath.Join(c.MkDir(), "lock")
cmd := exec.Command("flock", "--exclusive", lockPath, "sleep", "9999")
c.Assert(cmd.Start(), IsNil)
defer cmd.Process.Kill()

for i := 0; i < 10; i++ {
if osutil.FileExists(lockPath) {
break
}
time.Sleep(1 * time.Second)
}

lock, err := osutil.OpenLock(lockPath)
c.Assert(err, IsNil)
defer lock.Close()

c.Assert(lock.Lock(osutil.FLockNonBlocking), ErrorMatches, "resource temporarily unavailable")
}

0 comments on commit bc5a466

Please sign in to comment.