2
2
mirror of https://github.com/octoleo/restic.git synced 2024-11-15 17:47:21 +00:00
restic/cmd/restic/lock.go

138 lines
3.2 KiB
Go
Raw Normal View History

package main
import (
2017-06-04 09:16:55 +00:00
"context"
2015-07-12 20:10:01 +00:00
"sync"
"time"
2017-07-23 12:21:03 +00:00
"github.com/restic/restic/internal/debug"
"github.com/restic/restic/internal/errors"
2017-07-23 12:21:03 +00:00
"github.com/restic/restic/internal/repository"
2017-07-24 15:42:25 +00:00
"github.com/restic/restic/internal/restic"
)
2015-07-12 20:10:01 +00:00
var globalLocks struct {
locks []*restic.Lock
cancelRefresh chan struct{}
refreshWG sync.WaitGroup
sync.Mutex
sync.Once
2015-07-12 20:10:01 +00:00
}
2020-08-09 11:24:47 +00:00
func lockRepo(ctx context.Context, repo *repository.Repository) (*restic.Lock, error) {
return lockRepository(ctx, repo, false)
}
2020-08-09 11:24:47 +00:00
func lockRepoExclusive(ctx context.Context, repo *repository.Repository) (*restic.Lock, error) {
return lockRepository(ctx, repo, true)
}
2020-08-09 11:24:47 +00:00
func lockRepository(ctx context.Context, repo *repository.Repository, exclusive bool) (*restic.Lock, error) {
// make sure that a repository is unlocked properly and after cancel() was
// called by the cleanup handler in global.go
globalLocks.Do(func() {
AddCleanupHandler(unlockAll)
})
lockFn := restic.NewLock
if exclusive {
lockFn = restic.NewExclusiveLock
}
2020-08-09 11:24:47 +00:00
lock, err := lockFn(ctx, repo)
if err != nil {
return nil, errors.WithMessage(err, "unable to create lock in backend")
}
debug.Log("create lock %p (exclusive %v)", lock, exclusive)
2015-07-12 20:10:01 +00:00
globalLocks.Lock()
if globalLocks.cancelRefresh == nil {
2016-09-27 20:35:08 +00:00
debug.Log("start goroutine for lock refresh")
2015-07-12 20:10:01 +00:00
globalLocks.cancelRefresh = make(chan struct{})
globalLocks.refreshWG = sync.WaitGroup{}
globalLocks.refreshWG.Add(1)
go refreshLocks(&globalLocks.refreshWG, globalLocks.cancelRefresh)
}
globalLocks.locks = append(globalLocks.locks, lock)
globalLocks.Unlock()
return lock, err
}
2015-07-12 20:10:01 +00:00
var refreshInterval = 5 * time.Minute
func refreshLocks(wg *sync.WaitGroup, done <-chan struct{}) {
2016-09-27 20:35:08 +00:00
debug.Log("start")
2015-07-12 20:10:01 +00:00
defer func() {
wg.Done()
globalLocks.Lock()
globalLocks.cancelRefresh = nil
globalLocks.Unlock()
}()
ticker := time.NewTicker(refreshInterval)
for {
select {
case <-done:
2016-09-27 20:35:08 +00:00
debug.Log("terminate")
2015-07-12 20:10:01 +00:00
return
case <-ticker.C:
2016-09-27 20:35:08 +00:00
debug.Log("refreshing locks")
2015-07-12 20:10:01 +00:00
globalLocks.Lock()
for _, lock := range globalLocks.locks {
2017-06-04 09:16:55 +00:00
err := lock.Refresh(context.TODO())
2015-07-12 20:10:01 +00:00
if err != nil {
Warnf("unable to refresh lock: %v\n", err)
2015-07-12 20:10:01 +00:00
}
}
globalLocks.Unlock()
}
}
}
func unlockRepo(lock *restic.Lock) {
if lock == nil {
return
}
2015-07-12 20:10:01 +00:00
globalLocks.Lock()
defer globalLocks.Unlock()
for i := 0; i < len(globalLocks.locks); i++ {
if lock == globalLocks.locks[i] {
// remove the lock from the repo
debug.Log("unlocking repository with lock %v", lock)
if err := lock.Unlock(); err != nil {
debug.Log("error while unlocking: %v", err)
Warnf("error while unlocking: %v", err)
return
}
// remove the lock from the list of locks
2015-07-12 20:10:01 +00:00
globalLocks.locks = append(globalLocks.locks[:i], globalLocks.locks[i+1:]...)
return
}
}
debug.Log("unable to find lock %v in the global list of locks, ignoring", lock)
}
func unlockAll() error {
2015-07-12 20:10:01 +00:00
globalLocks.Lock()
defer globalLocks.Unlock()
2016-09-27 20:35:08 +00:00
debug.Log("unlocking %d locks", len(globalLocks.locks))
2015-07-12 20:10:01 +00:00
for _, lock := range globalLocks.locks {
if err := lock.Unlock(); err != nil {
2016-09-27 20:35:08 +00:00
debug.Log("error while unlocking: %v", err)
return err
}
2016-09-27 20:35:08 +00:00
debug.Log("successfully removed lock")
}
globalLocks.locks = globalLocks.locks[:0]
return nil
}