Skip to content

Commit 0b25f83

Browse files
committed
Do not block try lock on state mutex
TryLock should not block when file lock is held by other process and state lock is held by other coroutine which waits for file lock. Signed-off-by: Konstantin Khlebnikov <[email protected]>
1 parent f93db89 commit 0b25f83

File tree

2 files changed

+53
-1
lines changed

2 files changed

+53
-1
lines changed

storage/pkg/lockfile/lockfile.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -420,7 +420,10 @@ func (l *LockFile) tryLock(lType rawfilelock.LockType) error {
420420
if !success {
421421
return fmt.Errorf("resource temporarily unavailable")
422422
}
423-
l.stateMutex.Lock()
423+
if !l.stateMutex.TryLock() {
424+
rwMutexUnlocker()
425+
return fmt.Errorf("resource temporarily unavailable")
426+
}
424427
defer l.stateMutex.Unlock()
425428
if l.counter == 0 {
426429
// If we're the first reference on the lock, we need to open the file again.

storage/pkg/lockfile/lockfile_test.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,55 @@ func TestTryReadLockFile(t *testing.T) {
277277
assert.Nil(t, <-errChan)
278278
}
279279

280+
func TestTryLockState(t *testing.T) {
281+
l, err := getTempLockfile()
282+
require.NoError(t, err, "creating lock")
283+
defer os.Remove(l.name)
284+
285+
// Take a write lock somewhere.
286+
cmd, wc, rc, err := subLock(l)
287+
require.NoError(t, err)
288+
_, err = io.Copy(io.Discard, rc)
289+
require.NoError(t, err)
290+
291+
err = l.TryRLock()
292+
assert.NotNil(t, err)
293+
294+
// Lock and hold state mutex.
295+
locked := make(chan bool)
296+
go func() {
297+
locked <- false
298+
l.RLock()
299+
locked <- true
300+
l.Unlock()
301+
locked <- false
302+
}()
303+
304+
assert.False(t, <-locked)
305+
306+
// Wait state mutex is locked.
307+
for l.stateMutex.TryLock() {
308+
l.stateMutex.Unlock()
309+
time.Sleep(100 * time.Millisecond)
310+
}
311+
312+
// Try locks should fail without blocking.
313+
errChan := make(chan error)
314+
go func() {
315+
errChan <- l.TryRLock()
316+
errChan <- l.TryLock()
317+
}()
318+
assert.NotNil(t, <-errChan)
319+
assert.NotNil(t, <-errChan)
320+
321+
wc.Close()
322+
err = cmd.Wait()
323+
require.NoError(t, err)
324+
325+
assert.True(t, <-locked)
326+
assert.False(t, <-locked)
327+
}
328+
280329
func TestLockfileRead(t *testing.T) {
281330
l, err := getTempLockfile()
282331
require.Nil(t, err, "error getting temporary lock file")

0 commit comments

Comments
 (0)