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

134 lines
3.0 KiB
Go
Raw Normal View History

package main
import (
2017-06-04 09:16:55 +00:00
"context"
"fmt"
"os"
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
}
func lockRepo(repo *repository.Repository) (*restic.Lock, error) {
return lockRepository(repo, false)
}
func lockRepoExclusive(repo *repository.Repository) (*restic.Lock, error) {
return lockRepository(repo, true)
}
func lockRepository(repo *repository.Repository, exclusive bool) (*restic.Lock, error) {
lockFn := restic.NewLock
if exclusive {
lockFn = restic.NewExclusiveLock
}
2017-06-04 09:16:55 +00:00
lock, err := lockFn(context.TODO(), repo)
if err != nil {
return nil, errors.Fatalf("unable to create lock in backend: %v", err)
}
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 {
fmt.Fprintf(os.Stderr, "unable to refresh lock: %v\n", err)
}
}
globalLocks.Unlock()
}
}
}
func unlockRepo(lock *restic.Lock) error {
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)
return err
}
// 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 nil
}
}
debug.Log("unable to find lock %v in the global list of locks, ignoring", lock)
return nil
}
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
}
func init() {
2015-07-19 15:50:55 +00:00
AddCleanupHandler(unlockAll)
}