From 7f26757a40b27166b773aec306e63533e4db6747 Mon Sep 17 00:00:00 2001 From: Marius Date: Fri, 3 Mar 2017 11:16:56 +0100 Subject: [PATCH] Update lockfile dependency --- vendor/gopkg.in/Acconut/lockfile.v1/README.md | 22 ++- .../gopkg.in/Acconut/lockfile.v1/appveyor.yml | 5 +- .../gopkg.in/Acconut/lockfile.v1/lockfile.go | 146 +++++++++++++----- .../Acconut/lockfile.v1/lockfile_unix.go | 12 +- vendor/vendor.json | 6 +- 5 files changed, 127 insertions(+), 64 deletions(-) diff --git a/vendor/gopkg.in/Acconut/lockfile.v1/README.md b/vendor/gopkg.in/Acconut/lockfile.v1/README.md index 54ee19c..887bac0 100644 --- a/vendor/gopkg.in/Acconut/lockfile.v1/README.md +++ b/vendor/gopkg.in/Acconut/lockfile.v1/README.md @@ -2,13 +2,17 @@ lockfile ========= Handle locking via pid files. +*Attention:* This is a fork of [Ingo Oeser's amazing work](https://github.com/nightlyone/lockfile) +whose behavior differs a bit. While the original package allows a process to +obtain the same lock twice, this fork forbids this behavior. + [![Build Status Unix][1]][2] [![Build status Windows][3]][4] -[1]: https://secure.travis-ci.org/nightlyone/lockfile.png -[2]: https://travis-ci.org/nightlyone/lockfile -[3]: https://ci.appveyor.com/api/projects/status/7mojkmauj81uvp8u/branch/master?svg=true -[4]: https://ci.appveyor.com/project/nightlyone/lockfile/branch/master +[1]: https://secure.travis-ci.org/Acconut/lockfile.png +[2]: https://travis-ci.org/Acconut/lockfile +[3]: https://ci.appveyor.com/api/projects/status/bwy487h8cgue6up5?svg=true +[4]: https://ci.appveyor.com/project/Acconut/lockfile/branch/master @@ -19,7 +23,7 @@ For Windows suport, Go 1.4 or newer is required. Then run - go get github.com/nightlyone/lockfile + go get gopkg.in/Acconut/lockfile.v1 [5]: http://golang.org [6]: http://golang.org/doc/install/source @@ -31,12 +35,7 @@ BSD documentation ------------- -[package documentation at godoc.org](http://godoc.org/github.com/nightlyone/lockfile) - -install -------------------- - go get github.com/nightlyone/lockfile - +[package documentation at godoc.org](http://godoc.org/gopkg.in/Acconut/lockfile.v1) contributing ============ @@ -49,4 +48,3 @@ git commit hooks enable commit hooks via cd .git ; rm -rf hooks; ln -s ../git-hooks hooks ; cd .. - diff --git a/vendor/gopkg.in/Acconut/lockfile.v1/appveyor.yml b/vendor/gopkg.in/Acconut/lockfile.v1/appveyor.yml index cf72a58..60445bd 100644 --- a/vendor/gopkg.in/Acconut/lockfile.v1/appveyor.yml +++ b/vendor/gopkg.in/Acconut/lockfile.v1/appveyor.yml @@ -1,4 +1,4 @@ -clone_folder: c:\gopath\src\github.com\nightlyone\lockfile +clone_folder: c:\gopath\src\github.com\Acconut\lockfile environment: GOPATH: c:\gopath @@ -9,4 +9,5 @@ install: - go get -v -t ./... build_script: - - go test -v ./... + - go test -v . + - go vet . diff --git a/vendor/gopkg.in/Acconut/lockfile.v1/lockfile.go b/vendor/gopkg.in/Acconut/lockfile.v1/lockfile.go index bb5c221..ea8c349 100644 --- a/vendor/gopkg.in/Acconut/lockfile.v1/lockfile.go +++ b/vendor/gopkg.in/Acconut/lockfile.v1/lockfile.go @@ -1,24 +1,44 @@ -// Handle pid file based locking. +// Package lockfile handles pid file based locking. +// While a sync.Mutex helps against concurrency issues within a single process, +// this package is designed to help against concurrency issues between cooperating processes +// or serializing multiple invocations of the same process. package lockfile import ( "errors" "fmt" + "io" "io/ioutil" "os" "path/filepath" ) +// Lockfile is a pid file which can be locked type Lockfile string +// TemporaryError is a type of error where a retry after a random amount of sleep should help to mitigate it. +type TemporaryError string + +func (t TemporaryError) Error() string { return string(t) } + +// Temporary returns always true. +// It exists, so you can detect it via +// if te, ok := err.(interface{ Temporary() bool }); ok { +// fmt.Println("I am a temporay error situation, so wait and retry") +// } +func (t TemporaryError) Temporary() bool { return true } + +// Various errors returned by this package var ( - ErrBusy = errors.New("Locked by other process") // If you get this, retry after a short sleep might help - ErrNeedAbsPath = errors.New("Lockfiles must be given as absolute path names") - ErrInvalidPid = errors.New("Lockfile contains invalid pid for system") - ErrDeadOwner = errors.New("Lockfile contains pid of process not existent on this system anymore") + ErrBusy = TemporaryError("Locked by other process") // If you get this, retry after a short sleep might help + ErrNotExist = TemporaryError("Lockfile created, but doesn't exist") // If you get this, retry after a short sleep might help + ErrNeedAbsPath = errors.New("Lockfiles must be given as absolute path names") + ErrInvalidPid = errors.New("Lockfile contains invalid pid for system") + ErrDeadOwner = errors.New("Lockfile contains pid of process not existent on this system anymore") + ErrRogueDeletion = errors.New("Lockfile owned by me has been removed unexpectedly") ) -// Describe a new filename located at path. It is expected to be an absolute path +// New describes a new filename located at the given absolute path. func New(path string) (Lockfile, error) { if !filepath.IsAbs(path) { return Lockfile(""), ErrNeedAbsPath @@ -26,7 +46,7 @@ func New(path string) (Lockfile, error) { return Lockfile(path), nil } -// Who owns the lockfile? +// GetOwner returns who owns the lockfile. func (l Lockfile) GetOwner() (*os.Process, error) { name := string(l) @@ -36,58 +56,53 @@ func (l Lockfile) GetOwner() (*os.Process, error) { return nil, err } - var pid int - _, err = fmt.Sscanln(string(content), &pid) + // try hard for pids. If no pid, the lockfile is junk anyway and we delete it. + pid, err := scanPidLine(content) if err != nil { - return nil, ErrInvalidPid + return nil, err + } + running, err := isRunning(pid) + if err != nil { + return nil, err } - // try hard for pids. If no pid, the lockfile is junk anyway and we delete it. - if pid > 0 { - running, err := isRunning(pid) + if running { + proc, err := os.FindProcess(pid) if err != nil { return nil, err } - - if running { - proc, err := os.FindProcess(pid) - if err != nil { - return nil, err - } - return proc, nil - } else { - return nil, ErrDeadOwner - } - - } else { - return nil, ErrInvalidPid + return proc, nil } - panic("Not reached") + return nil, ErrDeadOwner + } -// Try to get Lockfile lock. Returns nil, if successful and and error describing the reason, it didn't work out. -// Please note, that existing lockfiles containing pids of dead processes and lockfiles containing no pid at all -// are deleted. +// TryLock tries to own the lock. +// It Returns nil, if successful and and error describing the reason, it didn't work out. +// Please note, that existing lockfiles containing pids of dead processes +// and lockfiles containing no pid at all are simply deleted. func (l Lockfile) TryLock() error { name := string(l) // This has been checked by New already. If we trigger here, // the caller didn't use New and re-implemented it's functionality badly. // So panic, that he might find this easily during testing. - if !filepath.IsAbs(string(name)) { + if !filepath.IsAbs(name) { panic(ErrNeedAbsPath) } tmplock, err := ioutil.TempFile(filepath.Dir(name), "") if err != nil { return err - } else { - defer tmplock.Close() - defer os.Remove(tmplock.Name()) } - _, err = tmplock.WriteString(fmt.Sprintf("%d\n", os.Getpid())) - if err != nil { + cleanup := func() { + _ = tmplock.Close() + _ = os.Remove(tmplock.Name()) + } + defer cleanup() + + if err := writePidLine(tmplock, os.Getpid()); err != nil { return err } @@ -100,6 +115,10 @@ func (l Lockfile) TryLock() error { } fiLock, err := os.Lstat(name) if err != nil { + // tell user that a retry would be a good idea + if os.IsNotExist(err) { + return ErrNotExist + } return err } @@ -114,6 +133,10 @@ func (l Lockfile) TryLock() error { // Other errors -> defensively fail and let caller handle this return err case nil: + // This fork differs from the upstream repository in this line. We do + // not want a process to obtain a lock if this lock is already help by + // the same process. Therefore, we always return ErrBusy if the lockfile + // contains a non-dead PID. return ErrBusy case ErrDeadOwner, ErrInvalidPid: // cases we can fix below @@ -122,14 +145,57 @@ func (l Lockfile) TryLock() error { // clean stale/invalid lockfile err = os.Remove(name) if err != nil { - return err + // If it doesn't exist, then it doesn't matter who removed it. + if !os.IsNotExist(err) { + return err + } } - // now that we cleaned up the stale lockfile, let's recurse + // now that the stale lockfile is gone, let's recurse return l.TryLock() } -// Release a lock again. Returns any error that happend during release of lock. +// Unlock a lock again, if we owned it. Returns any error that happend during release of lock. func (l Lockfile) Unlock() error { - return os.Remove(string(l)) + proc, err := l.GetOwner() + switch err { + case ErrInvalidPid, ErrDeadOwner: + return ErrRogueDeletion + case nil: + if proc.Pid == os.Getpid() { + // we really own it, so let's remove it. + return os.Remove(string(l)) + } + // Not owned by me, so don't delete it. + return ErrRogueDeletion + default: + // This is an application error or system error. + // So give a better error for logging here. + if os.IsNotExist(err) { + return ErrRogueDeletion + } + // Other errors -> defensively fail and let caller handle this + return err + } +} + +func writePidLine(w io.Writer, pid int) error { + _, err := io.WriteString(w, fmt.Sprintf("%d\n", pid)) + return err +} + +func scanPidLine(content []byte) (int, error) { + if len(content) == 0 { + return 0, ErrInvalidPid + } + + var pid int + if _, err := fmt.Sscanln(string(content), &pid); err != nil { + return 0, ErrInvalidPid + } + + if pid <= 0 { + return 0, ErrInvalidPid + } + return pid, nil } diff --git a/vendor/gopkg.in/Acconut/lockfile.v1/lockfile_unix.go b/vendor/gopkg.in/Acconut/lockfile.v1/lockfile_unix.go index 36c46eb..742b041 100644 --- a/vendor/gopkg.in/Acconut/lockfile.v1/lockfile_unix.go +++ b/vendor/gopkg.in/Acconut/lockfile.v1/lockfile_unix.go @@ -11,12 +11,10 @@ func isRunning(pid int) (bool, error) { proc, err := os.FindProcess(pid) if err != nil { return false, err - } else { - err := proc.Signal(syscall.Signal(0)) - if err == nil { - return true, nil - } else { - return false, nil - } } + + if err := proc.Signal(syscall.Signal(0)); err != nil { + return false, nil + } + return true, nil } diff --git a/vendor/vendor.json b/vendor/vendor.json index cb84bf4..83d0304 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -33,10 +33,10 @@ "revisionTime": "2016-06-15T09:26:46Z" }, { - "checksumSHA1": "0zBf3a7Gzqypt1sWefwx4NkOEIc=", + "checksumSHA1": "BpFC/4q4mc/SfCvsXGzCF8s9fSE=", "path": "gopkg.in/Acconut/lockfile.v1", - "revision": "941fc61ce4d67dd432b94ed0bd445ad5fb9391a9", - "revisionTime": "2016-05-20T15:30:20Z" + "revision": "ea910fd69bd541bb592c5729c94f595c696413ab", + "revisionTime": "2017-03-03T10:08:22Z" } ], "rootPath": "github.com/tus/tusd"