github.com/advanderveer/restic@v0.8.1-0.20171209104529-42a8c19aaea6/cmd/restic/lock.go (about) 1 package main 2 3 import ( 4 "context" 5 "fmt" 6 "os" 7 "sync" 8 "time" 9 10 "github.com/restic/restic/internal/debug" 11 "github.com/restic/restic/internal/errors" 12 "github.com/restic/restic/internal/repository" 13 "github.com/restic/restic/internal/restic" 14 ) 15 16 var globalLocks struct { 17 locks []*restic.Lock 18 cancelRefresh chan struct{} 19 refreshWG sync.WaitGroup 20 sync.Mutex 21 } 22 23 func lockRepo(repo *repository.Repository) (*restic.Lock, error) { 24 return lockRepository(repo, false) 25 } 26 27 func lockRepoExclusive(repo *repository.Repository) (*restic.Lock, error) { 28 return lockRepository(repo, true) 29 } 30 31 func lockRepository(repo *repository.Repository, exclusive bool) (*restic.Lock, error) { 32 lockFn := restic.NewLock 33 if exclusive { 34 lockFn = restic.NewExclusiveLock 35 } 36 37 lock, err := lockFn(context.TODO(), repo) 38 if err != nil { 39 return nil, errors.Fatalf("unable to create lock in backend: %v", err) 40 } 41 debug.Log("create lock %p (exclusive %v)", lock, exclusive) 42 43 globalLocks.Lock() 44 if globalLocks.cancelRefresh == nil { 45 debug.Log("start goroutine for lock refresh") 46 globalLocks.cancelRefresh = make(chan struct{}) 47 globalLocks.refreshWG = sync.WaitGroup{} 48 globalLocks.refreshWG.Add(1) 49 go refreshLocks(&globalLocks.refreshWG, globalLocks.cancelRefresh) 50 } 51 52 globalLocks.locks = append(globalLocks.locks, lock) 53 globalLocks.Unlock() 54 55 return lock, err 56 } 57 58 var refreshInterval = 5 * time.Minute 59 60 func refreshLocks(wg *sync.WaitGroup, done <-chan struct{}) { 61 debug.Log("start") 62 defer func() { 63 wg.Done() 64 globalLocks.Lock() 65 globalLocks.cancelRefresh = nil 66 globalLocks.Unlock() 67 }() 68 69 ticker := time.NewTicker(refreshInterval) 70 71 for { 72 select { 73 case <-done: 74 debug.Log("terminate") 75 return 76 case <-ticker.C: 77 debug.Log("refreshing locks") 78 globalLocks.Lock() 79 for _, lock := range globalLocks.locks { 80 err := lock.Refresh(context.TODO()) 81 if err != nil { 82 fmt.Fprintf(os.Stderr, "unable to refresh lock: %v\n", err) 83 } 84 } 85 globalLocks.Unlock() 86 } 87 } 88 } 89 90 func unlockRepo(lock *restic.Lock) error { 91 globalLocks.Lock() 92 defer globalLocks.Unlock() 93 94 debug.Log("unlocking repository with lock %p", lock) 95 if err := lock.Unlock(); err != nil { 96 debug.Log("error while unlocking: %v", err) 97 return err 98 } 99 100 for i := 0; i < len(globalLocks.locks); i++ { 101 if lock == globalLocks.locks[i] { 102 globalLocks.locks = append(globalLocks.locks[:i], globalLocks.locks[i+1:]...) 103 return nil 104 } 105 } 106 107 return nil 108 } 109 110 func unlockAll() error { 111 globalLocks.Lock() 112 defer globalLocks.Unlock() 113 114 debug.Log("unlocking %d locks", len(globalLocks.locks)) 115 for _, lock := range globalLocks.locks { 116 if err := lock.Unlock(); err != nil { 117 debug.Log("error while unlocking: %v", err) 118 return err 119 } 120 debug.Log("successfully removed lock") 121 } 122 123 return nil 124 } 125 126 func init() { 127 AddCleanupHandler(unlockAll) 128 }