Add lock conflict check

This commit is contained in:
Alexander Neumann 2015-06-27 14:26:33 +02:00
parent a217f51f2c
commit fba912440d
2 changed files with 90 additions and 0 deletions

13
lock.go
View file

@ -52,6 +52,8 @@ func NewExclusiveLock(repo *repository.Repository) (*Lock, error) {
return newLock(repo, true) return newLock(repo, true)
} }
const waitBeforeLockCheck = 200 * time.Millisecond
func newLock(repo *repository.Repository, excl bool) (*Lock, error) { func newLock(repo *repository.Repository, excl bool) (*Lock, error) {
lock := &Lock{ lock := &Lock{
Time: time.Now(), Time: time.Now(),
@ -78,6 +80,13 @@ func newLock(repo *repository.Repository, excl bool) (*Lock, error) {
return nil, err return nil, err
} }
time.Sleep(waitBeforeLockCheck)
if err = lock.checkForOtherLocks(); err != nil {
lock.Unlock()
return nil, ErrAlreadyLocked
}
return lock, nil return lock, nil
} }
@ -111,6 +120,10 @@ func (l *Lock) fillUserInfo() error {
// exclusive lock is found. // exclusive lock is found.
func (l *Lock) checkForOtherLocks() error { func (l *Lock) checkForOtherLocks() error {
return eachLock(l.repo, func(id backend.ID, lock *Lock, err error) error { return eachLock(l.repo, func(id backend.ID, lock *Lock, err error) error {
if id.Equal(l.lockID) {
return nil
}
// ignore locks that cannot be loaded // ignore locks that cannot be loaded
if err != nil { if err != nil {
return nil return nil

View file

@ -2,6 +2,7 @@ package restic_test
import ( import (
"os" "os"
"sync"
"testing" "testing"
"time" "time"
@ -168,3 +169,79 @@ func TestLockWithStaleLock(t *testing.T) {
OK(t, removeLock(repo, id2)) OK(t, removeLock(repo, id2))
} }
func TestLockConflictingExclusiveLocks(t *testing.T) {
repo := SetupRepo()
defer TeardownRepo(repo)
for _, jobs := range []int{5, 23, 200} {
var wg sync.WaitGroup
errch := make(chan error, jobs)
f := func() {
defer wg.Done()
lock, err := restic.NewExclusiveLock(repo)
errch <- err
OK(t, lock.Unlock())
}
for i := 0; i < jobs; i++ {
wg.Add(1)
go f()
}
errors := 0
for i := 0; i < jobs; i++ {
err := <-errch
if err != nil {
errors++
}
}
wg.Wait()
Assert(t, errors == jobs-1,
"Expected %d errors, got %d", jobs-1, errors)
}
}
func TestLockConflictingLocks(t *testing.T) {
repo := SetupRepo()
defer TeardownRepo(repo)
var wg sync.WaitGroup
errch := make(chan error, 2)
wg.Add(2)
go func() {
defer wg.Done()
lock, err := restic.NewExclusiveLock(repo)
errch <- err
OK(t, lock.Unlock())
}()
go func() {
defer wg.Done()
lock, err := restic.NewLock(repo)
errch <- err
OK(t, lock.Unlock())
}()
errors := 0
for i := 0; i < 2; i++ {
err := <-errch
if err != nil {
errors++
}
}
wg.Wait()
Assert(t, errors == 1,
"Expected exactly one errors, got %d", errors)
}