gopkg.in/Masterminds/glide.v0@v0.13.3/cache/global_lock.go (about) 1 package cache 2 3 import ( 4 "encoding/json" 5 "io/ioutil" 6 "os" 7 "os/signal" 8 "path/filepath" 9 "time" 10 11 "github.com/Masterminds/glide/msg" 12 gpath "github.com/Masterminds/glide/path" 13 ) 14 15 var isStarted bool 16 17 // If the global cache lock file should be written 18 var shouldWriteLock = true 19 20 // SystemLock starts a system rather than application lock. This way multiple 21 // app instances don't cause race conditions when working in the cache. 22 func SystemLock() error { 23 if isStarted { 24 return nil 25 } 26 err := waitOnLock() 27 if err != nil { 28 return err 29 } 30 err = startLock() 31 isStarted = true 32 return err 33 } 34 35 // SystemUnlock removes the system wide Glide cache lock. 36 func SystemUnlock() { 37 lockdone <- struct{}{} 38 os.Remove(lockFileName) 39 } 40 41 var lockdone = make(chan struct{}, 1) 42 43 type lockdata struct { 44 Comment string `json:"comment"` 45 Pid int `json:"pid"` 46 Time string `json:"time"` 47 } 48 49 var lockFileName = filepath.Join(gpath.Home(), "lock.json") 50 51 // Write a lock for now. 52 func writeLock() error { 53 54 // If the lock should not be written exit immediately. This happens in cases 55 // where shutdown/clean is happening. 56 if !shouldWriteLock { 57 return nil 58 } 59 60 ld := &lockdata{ 61 Comment: "File managed by Glide (https://glide.sh)", 62 Pid: os.Getpid(), 63 Time: time.Now().Format(time.RFC3339Nano), 64 } 65 66 out, err := json.Marshal(ld) 67 if err != nil { 68 return err 69 } 70 err = ioutil.WriteFile(lockFileName, out, 0755) 71 return err 72 } 73 74 func startLock() error { 75 err := writeLock() 76 if err != nil { 77 return err 78 } 79 80 go func() { 81 for { 82 select { 83 case <-lockdone: 84 return 85 default: 86 time.Sleep(10 * time.Second) 87 err := writeLock() 88 if err != nil { 89 msg.Die("Error using Glide lock: %s", err) 90 } 91 } 92 } 93 }() 94 95 // Capture ctrl-c or other interruptions then clean up the global lock. 96 ch := make(chan os.Signal) 97 signal.Notify(ch, os.Interrupt, os.Kill) 98 go func(cc <-chan os.Signal) { 99 s := <-cc 100 shouldWriteLock = false 101 SystemUnlock() 102 103 // Exiting with the expected exit codes when we can. 104 if s == os.Interrupt { 105 os.Exit(130) 106 } else if s == os.Kill { 107 os.Exit(137) 108 } else { 109 os.Exit(1) 110 } 111 }(ch) 112 113 return nil 114 } 115 116 func waitOnLock() error { 117 var announced bool 118 for { 119 fi, err := os.Stat(lockFileName) 120 if err != nil && os.IsNotExist(err) { 121 return nil 122 } else if err != nil { 123 return err 124 } 125 126 diff := time.Now().Sub(fi.ModTime()) 127 if diff.Seconds() > 15 { 128 return nil 129 } 130 131 if !announced { 132 announced = true 133 msg.Info("Waiting on Glide global cache access") 134 } 135 136 // Check on the lock file every second. 137 time.Sleep(time.Second) 138 } 139 }