Skip to content

Commit

Permalink
Merge pull request #11 from gonix/try-lock-context
Browse files Browse the repository at this point in the history
add .TryLockContext method to *Flock
  • Loading branch information
theckman committed Oct 30, 2017
2 parents a7172a0 + 8f8e310 commit 6f9a681
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 0 deletions.
21 changes: 21 additions & 0 deletions flock.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@
package flock

import (
"context"
"os"
"sync"
"time"
)

// Flock is the struct type to handle file locking. All fields are unexported,
Expand Down Expand Up @@ -46,6 +48,25 @@ func (f *Flock) String() string {
return f.path
}

// TryLockContext repeatedly tries locking until one of the conditions is met:
// TryLock succeeds, TryLock fails with error, or Context Done channel is closed.
func (f *Flock) TryLockContext(ctx context.Context, retryDelay time.Duration) (bool, error) {
if ctx.Err() != nil {
return false, ctx.Err()
}
for {
if ok, err := f.TryLock(); ok || err != nil {
return ok, err
}
select {
case <-ctx.Done():
return false, ctx.Err()
case <-time.After(retryDelay):
// try again
}
}
}

func (f *Flock) setFh() error {
// open a new os.File instance
// create it if it doesn't exist, truncate it if it does exist, open the file read-write
Expand Down
27 changes: 27 additions & 0 deletions flock_example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
package flock_test

import (
"context"
"fmt"
"time"

"github.com/theckman/go-flock"
)
Expand Down Expand Up @@ -45,3 +47,28 @@ func ExampleFlock_TryLock() {
// Output: path: /tmp/go-lock.lock; locked: true
// path: /tmp/go-lock.lock; locked: false
}

func ExampleFlock_TryLockContext() {
// should probably put these in /var/lock
fileLock := flock.NewFlock("/tmp/go-lock.lock")

lockCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
locked, err := fileLock.TryLockContext(lockCtx, 678*time.Millisecond)

if err != nil {
// handle locking error
}

if locked {
fmt.Printf("path: %s; locked: %v\n", fileLock.Path(), fileLock.Locked())

if err := fileLock.Unlock(); err != nil {
// handle unlock error
}
}

fmt.Printf("path: %s; locked: %v\n", fileLock.Path(), fileLock.Locked())
// Output: path: /tmp/go-lock.lock; locked: true
// path: /tmp/go-lock.lock; locked: false
}
23 changes: 23 additions & 0 deletions flock_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
package flock_test

import (
"context"
"io/ioutil"
"os"
"testing"
"time"

"github.com/theckman/go-flock"

Expand Down Expand Up @@ -90,6 +92,27 @@ func (t *TestSuite) TestFlock_TryLock(c *C) {
c.Check(locked, Equals, false)
}

func (t *TestSuite) TestFlock_TryLockContext(c *C) {
// happy path
ctx, cancel := context.WithCancel(context.Background())
locked, err := t.flock.TryLockContext(ctx, time.Second)
c.Assert(err, IsNil)
c.Check(locked, Equals, true)

// context already canceled
cancel()
locked, err = flock.NewFlock(t.path).TryLockContext(ctx, time.Second)
c.Assert(err, Equals, context.Canceled)
c.Check(locked, Equals, false)

// timeout
ctx, cancel = context.WithTimeout(context.Background(), 10*time.Millisecond)
defer cancel()
locked, err = flock.NewFlock(t.path).TryLockContext(ctx, time.Second)
c.Assert(err, Equals, context.DeadlineExceeded)
c.Check(locked, Equals, false)
}

func (t *TestSuite) TestFlock_Unlock(c *C) {
var err error

Expand Down

0 comments on commit 6f9a681

Please sign in to comment.