|  | package cache | 
|  |  | 
|  | import ( | 
|  | "encoding/json" | 
|  | "io/ioutil" | 
|  | "os" | 
|  | "path/filepath" | 
|  | "time" | 
|  |  | 
|  | "github.com/Masterminds/glide/msg" | 
|  | gpath "github.com/Masterminds/glide/path" | 
|  | ) | 
|  |  | 
|  | var isStarted bool | 
|  |  | 
|  | // SystemLock starts a system rather than application lock. This way multiple | 
|  | // app instances don't cause race conditions when working in the cache. | 
|  | func SystemLock() error { | 
|  | if isStarted { | 
|  | return nil | 
|  | } | 
|  | err := waitOnLock() | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  | err = startLock() | 
|  | isStarted = true | 
|  | return err | 
|  | } | 
|  |  | 
|  | // SystemUnlock removes the system wide Glide cache lock. | 
|  | func SystemUnlock() { | 
|  | lockdone <- struct{}{} | 
|  | os.Remove(lockFileName) | 
|  | } | 
|  |  | 
|  | var lockdone = make(chan struct{}, 1) | 
|  |  | 
|  | type lockdata struct { | 
|  | Comment string `json:"comment"` | 
|  | Pid     int    `json:"pid"` | 
|  | Time    string `json:"time"` | 
|  | } | 
|  |  | 
|  | var lockFileName = filepath.Join(gpath.Home(), "lock.json") | 
|  |  | 
|  | // Write a lock for now. | 
|  | func writeLock() error { | 
|  | ld := &lockdata{ | 
|  | Comment: "File managed by Glide (https://glide.sh)", | 
|  | Pid:     os.Getpid(), | 
|  | Time:    time.Now().Format(time.RFC3339Nano), | 
|  | } | 
|  |  | 
|  | out, err := json.Marshal(ld) | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  | err = ioutil.WriteFile(lockFileName, out, 0755) | 
|  | return err | 
|  | } | 
|  |  | 
|  | func startLock() error { | 
|  | err := writeLock() | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  |  | 
|  | go func() { | 
|  | for { | 
|  | select { | 
|  | case <-lockdone: | 
|  | return | 
|  | default: | 
|  | time.Sleep(10 * time.Second) | 
|  | err := writeLock() | 
|  | if err != nil { | 
|  | msg.Die("Error using Glide lock: %s", err) | 
|  | } | 
|  | } | 
|  | } | 
|  | }() | 
|  |  | 
|  | return nil | 
|  | } | 
|  |  | 
|  | func waitOnLock() error { | 
|  | var announced bool | 
|  | for { | 
|  | fi, err := os.Stat(lockFileName) | 
|  | if err != nil && os.IsNotExist(err) { | 
|  | return nil | 
|  | } else if err != nil { | 
|  | return err | 
|  | } | 
|  |  | 
|  | diff := time.Now().Sub(fi.ModTime()) | 
|  | if diff.Seconds() > 15 { | 
|  | return nil | 
|  | } | 
|  |  | 
|  | if !announced { | 
|  | announced = true | 
|  | msg.Info("Waiting on Glide global cache access") | 
|  | } | 
|  |  | 
|  | // Check on the lock file every 15 seconds. | 
|  | // TODO(mattfarina): should this be a different length? | 
|  | time.Sleep(time.Second) | 
|  | } | 
|  | } |