Better comments, Unlock test.
Add better comments for several functions, fixing errors and clarifying
intent.
Add a test for a lock's Handle and its Unlock method. Clarify that
Unlock is intended to panic if called multiple times after a single
success. Additionally, add implementations for this feature.
diff --git a/fslock/lock.go b/fslock/lock.go
index d29011f..0b96a7c 100644
--- a/fslock/lock.go
+++ b/fslock/lock.go
@@ -13,7 +13,9 @@
var ErrLockHeld = errors.New("fslock: lock is held")
// Handle is a reference to a held lock. It must be released via Unlock when
-// finished.
+// finished. Multiple calls to Unlock will panic.
+//
+// Handle is NOT safe for concurrent use.
type Handle interface {
// Unlock releases the held lock.
//
@@ -23,10 +25,13 @@
Unlock() error
}
-// DelayFunc is used for the Delay field in a Lock.
+// Blocker is used for the Delay field in a Lock.
type Blocker func() error
// L describes a filesystem lock.
+//
+// L's fields should not be modified concurrently, but L's methods are safe
+// for concurrent use.
type L struct {
// Path is the path of the file to lock.
Path string
@@ -47,9 +52,10 @@
// Block is the configured blocking function.
//
// If not nil, an attempt to acquire the lock will loop indefinitely until an
- // error is encountered or the lock is acquired. Block will be called in
- // between each acquire attempt, and should delay and/or cancel the
- // acquisition.
+ // error other than ErrLockHeld is encountered (fatal) or the lock is
+ // acquired. Block will be called each time a lock attempt returns
+ // ErrLockHeld, and should delay and/or cancel the acquisition by returning
+ // nil or an error code respectively.
//
// If Block returns an error, it will be propagated as the error result of the
// locking attempt.
diff --git a/fslock/lock_posix.go b/fslock/lock_posix.go
index 6409626..45fb363 100644
--- a/fslock/lock_posix.go
+++ b/fslock/lock_posix.go
@@ -118,6 +118,7 @@
return err
}
delete(l.pls.held, l.ino)
+ l.pls = nil
return nil
}
diff --git a/fslock/lock_test.go b/fslock/lock_test.go
index 39f5b44..c48d925 100644
--- a/fslock/lock_test.go
+++ b/fslock/lock_test.go
@@ -368,3 +368,38 @@
}
})
}
+
+// TestUnlock tests a lock's Unlock function.
+func TestUnlock(t *testing.T) {
+ t.Parallel()
+
+ withTempDir(t, "content", func(tdir string) {
+ lock := filepath.Join(tdir, "lock")
+ h, err := Lock(lock)
+ if err != nil {
+ t.Fatalf("failed to acquire lock: %v", err)
+ }
+ if h == nil {
+ t.Fatal("lock did not return a Handle")
+ }
+
+ if err := h.Unlock(); err != nil {
+ t.Fatalf("Unlock returned an error: %v", err)
+ }
+
+ var panicVal interface{}
+ err = func() error {
+ defer func() {
+ panicVal = recover()
+ }()
+ return h.Unlock()
+ }()
+ if err != nil {
+ t.Fatalf("second Unlock returned an error: %v", err)
+ }
+ if panicVal == nil {
+ t.Fatal("second Unlock did not panic")
+ }
+ t.Logf("panicked with: %v", panicVal)
+ })
+}
diff --git a/fslock/lock_windows.go b/fslock/lock_windows.go
index eacaef5..1e05ae5 100644
--- a/fslock/lock_windows.go
+++ b/fslock/lock_windows.go
@@ -32,7 +32,16 @@
fd *os.File
}
-func (h winLockHandle) Unlock() error { return h.fd.Close() }
+func (h winLockHandle) Unlock() error {
+ if h.fd == nil {
+ panic("lock is not held")
+ }
+ if err := h.fd.Close(); err != nil {
+ return err
+ }
+ h.fd = nil
+ return nil
+}
func exclusiveGetOrCreateFile(path string) (*os.File, bool, error) {
mod := syscall.NewLazyDLL("kernel32.dll")