Skip to content
This repository has been archived by the owner on Sep 9, 2020. It is now read-only.

Commit

Permalink
Merge pull request #853 from matjam/fix820-3
Browse files Browse the repository at this point in the history
source_manager.go handle locking better
  • Loading branch information
sdboyer committed Jul 21, 2017
2 parents 43c2f05 + 6bb33ee commit 88660b3
Show file tree
Hide file tree
Showing 13 changed files with 740 additions and 16 deletions.
8 changes: 7 additions & 1 deletion Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 0 additions & 4 deletions internal/gps/manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -713,10 +713,6 @@ func TestSignalHandling(t *testing.T) {
t.Error("Releasing flag did not get set")
}

lpath := filepath.Join(sm.cachedir, "sm.lock")
if _, err := os.Stat(lpath); err == nil {
t.Fatal("Expected error on statting what should be an absent lock file")
}
clean()

// Test again, this time with a running call
Expand Down
58 changes: 47 additions & 11 deletions internal/gps/source_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"time"

"github.com/golang/dep/internal/gps/pkgtree"
"github.com/nightlyone/lockfile"
"github.com/pkg/errors"
"github.com/sdboyer/constext"
)
Expand Down Expand Up @@ -115,7 +116,7 @@ func (p ProjectAnalyzerInfo) String() string {
// tools; control via dependency injection is intended to be sufficient.
type SourceMgr struct {
cachedir string // path to root of cache dir
lf *os.File // handle for the sm lock file on disk
lf *lockfile.Lockfile // handle for the sm lock file on disk
suprvsr *supervisor // subsystem that supervises running calls/io
cancelAll context.CancelFunc // cancel func to kill all running work
deduceCoord *deductionCoordinator // subsystem that manages import path deduction
Expand Down Expand Up @@ -153,21 +154,56 @@ func NewSourceManager(cachedir string) (*SourceMgr, error) {
return nil, err
}

// Fix for #820
//
// Consult https://godoc.org/github.com/nightlyone/lockfile for the lockfile
// behaviour. It's magic. It deals with stale processes, and if there is
// a process keeping the lock busy, it will pass back a temporary error that
// we can spin on.

glpath := filepath.Join(cachedir, "sm.lock")
_, err = os.Stat(glpath)
if err == nil {
lockfile, err := lockfile.New(glpath)
if err != nil {
return nil, CouldNotCreateLockError{
Path: glpath,
Err: fmt.Errorf("cache lock file %s exists - another process crashed or is still running?", glpath),
Err: fmt.Errorf("unable to create lock %s: %s", glpath, err.Error()),
}
}

fi, err := os.OpenFile(glpath, os.O_CREATE|os.O_EXCL, 0600) // is 0600 sane for this purpose?
if err != nil {
return nil, CouldNotCreateLockError{
Path: glpath,
Err: fmt.Errorf("err on attempting to create global cache lock: %s", err),
process, err := lockfile.GetOwner()
if err == nil {
// If we didn't get an error, then the lockfile exists already. We should
// check to see if it's us already:
if process.Pid == os.Getpid() {
return nil, CouldNotCreateLockError{
Path: glpath,
Err: fmt.Errorf("lockfile %s already locked by this process", glpath),
}
}

// There is a lockfile, but it's owned by someone else. We'll try to lock
// it anyway.
}

// If it's a TemporaryError, we retry every second. Otherwise, we fail
// permanently.
//
// TODO: After some time, we should emit some kind of warning that we're waiting
// for the lockfile to be released. #534 should be address before we will do that.

err = lockfile.TryLock()
for err != nil {
if _, ok := err.(interface {
Temporary() bool
}); ok {
time.Sleep(time.Second * 1)
} else {
return nil, CouldNotCreateLockError{
Path: glpath,
Err: fmt.Errorf("unable to lock %s: %s", glpath, err.Error()),
}
}
err = lockfile.TryLock()
}

ctx, cf := context.WithCancel(context.TODO())
Expand All @@ -176,7 +212,7 @@ func NewSourceManager(cachedir string) (*SourceMgr, error) {

sm := &SourceMgr{
cachedir: cachedir,
lf: fi,
lf: &lockfile,
suprvsr: superv,
cancelAll: cf,
deduceCoord: deducer,
Expand Down Expand Up @@ -314,7 +350,7 @@ func (sm *SourceMgr) doRelease() {
sm.suprvsr.wait()

// Close the file handle for the lock file and remove it from disk
sm.lf.Close()
sm.lf.Unlock()
os.Remove(filepath.Join(sm.cachedir, "sm.lock"))

// Close the qch, if non-nil, so the signal handlers run out. This will
Expand Down
27 changes: 27 additions & 0 deletions vendor/github.com/nightlyone/lockfile/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions vendor/github.com/nightlyone/lockfile/.gitmodules

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions vendor/github.com/nightlyone/lockfile/.travis.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 19 additions & 0 deletions vendor/github.com/nightlyone/lockfile/LICENSE

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

52 changes: 52 additions & 0 deletions vendor/github.com/nightlyone/lockfile/README.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions vendor/github.com/nightlyone/lockfile/appveyor.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 88660b3

Please sign in to comment.