Lock by flock to prevent parallel runs (#812)
This commit is contained in:
parent
9ba730e989
commit
64b62667c0
1
go.mod
1
go.mod
@ -8,6 +8,7 @@ require (
|
|||||||
github.com/fatih/color v1.7.0
|
github.com/fatih/color v1.7.0
|
||||||
github.com/go-critic/go-critic v0.3.5-0.20190904082202-d79a9f0c64db
|
github.com/go-critic/go-critic v0.3.5-0.20190904082202-d79a9f0c64db
|
||||||
github.com/go-lintpack/lintpack v0.5.2
|
github.com/go-lintpack/lintpack v0.5.2
|
||||||
|
github.com/gofrs/flock v0.0.0-20190320160742-5135e617513b
|
||||||
github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2
|
github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2
|
||||||
github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a
|
github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a
|
||||||
github.com/golangci/errcheck v0.0.0-20181223084120-ef45e06d44b6
|
github.com/golangci/errcheck v0.0.0-20181223084120-ef45e06d44b6
|
||||||
|
2
go.sum
2
go.sum
@ -66,6 +66,8 @@ github.com/go-toolsmith/typep v1.0.0 h1:zKymWyA1TRYvqYrYDrfEMZULyrhcnGY3x7LDKU2X
|
|||||||
github.com/go-toolsmith/typep v1.0.0/go.mod h1:JSQCQMUPdRlMZFswiq3TGpNp1GMktqkR2Ns5AIQkATU=
|
github.com/go-toolsmith/typep v1.0.0/go.mod h1:JSQCQMUPdRlMZFswiq3TGpNp1GMktqkR2Ns5AIQkATU=
|
||||||
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
||||||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||||
|
github.com/gofrs/flock v0.0.0-20190320160742-5135e617513b h1:ekuhfTjngPhisSjOJ0QWKpPQE8/rbknHaes6WVJj5Hw=
|
||||||
|
github.com/gofrs/flock v0.0.0-20190320160742-5135e617513b/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
|
||||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||||
github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
|
github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
|
||||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||||
|
@ -2,11 +2,13 @@ package commands
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/golangci/golangci-lint/internal/cache"
|
"github.com/golangci/golangci-lint/internal/cache"
|
||||||
|
|
||||||
@ -15,6 +17,8 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
|
|
||||||
|
"github.com/gofrs/flock"
|
||||||
|
|
||||||
"github.com/golangci/golangci-lint/internal/pkgcache"
|
"github.com/golangci/golangci-lint/internal/pkgcache"
|
||||||
"github.com/golangci/golangci-lint/pkg/config"
|
"github.com/golangci/golangci-lint/pkg/config"
|
||||||
"github.com/golangci/golangci-lint/pkg/fsutils"
|
"github.com/golangci/golangci-lint/pkg/fsutils"
|
||||||
@ -48,6 +52,7 @@ type Executor struct {
|
|||||||
sw *timeutils.Stopwatch
|
sw *timeutils.Stopwatch
|
||||||
|
|
||||||
loadGuard *load.Guard
|
loadGuard *load.Guard
|
||||||
|
flock *flock.Flock
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewExecutor(version, commit, date string) *Executor {
|
func NewExecutor(version, commit, date string) *Executor {
|
||||||
@ -62,6 +67,9 @@ func NewExecutor(version, commit, date string) *Executor {
|
|||||||
|
|
||||||
e.debugf("Starting execution...")
|
e.debugf("Starting execution...")
|
||||||
e.log = report.NewLogWrapper(logutils.NewStderrLog(""), &e.reportData)
|
e.log = report.NewLogWrapper(logutils.NewStderrLog(""), &e.reportData)
|
||||||
|
if ok := e.acquireFileLock(); !ok {
|
||||||
|
e.log.Fatalf("Parallel golangci-lint is running")
|
||||||
|
}
|
||||||
|
|
||||||
// to setup log level early we need to parse config from command line extra time to
|
// to setup log level early we need to parse config from command line extra time to
|
||||||
// find `-v` option
|
// find `-v` option
|
||||||
@ -195,3 +203,24 @@ func computeConfigSalt(cfg *config.Config) ([]byte, error) {
|
|||||||
}
|
}
|
||||||
return h.Sum(nil), nil
|
return h.Sum(nil), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *Executor) acquireFileLock() bool {
|
||||||
|
lockFile := os.TempDir() + "/golangci-lint.lock"
|
||||||
|
e.debugf("Locking on file %s...", lockFile)
|
||||||
|
f := flock.New(lockFile)
|
||||||
|
ctx, finish := context.WithTimeout(context.Background(), time.Minute)
|
||||||
|
defer finish()
|
||||||
|
|
||||||
|
if ok, _ := f.TryLockContext(ctx, time.Second*3); !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
e.flock = f
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Executor) releaseFileLock() {
|
||||||
|
if err := e.flock.Unlock(); err != nil {
|
||||||
|
e.debugf("Failed to unlock on file: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -73,6 +73,7 @@ func (e *Executor) persistentPostRun(_ *cobra.Command, _ []string) {
|
|||||||
trace.Stop()
|
trace.Stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
e.releaseFileLock()
|
||||||
os.Exit(e.exitCode)
|
os.Exit(e.exitCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -453,7 +453,6 @@ func watchResources(ctx context.Context, done chan struct{}, logger logutils.Log
|
|||||||
const MB = 1024 * 1024
|
const MB = 1024 * 1024
|
||||||
|
|
||||||
track := func() {
|
track := func() {
|
||||||
debugf("Starting memory tracing iteration ...")
|
|
||||||
var m runtime.MemStats
|
var m runtime.MemStats
|
||||||
runtime.ReadMemStats(&m)
|
runtime.ReadMemStats(&m)
|
||||||
|
|
||||||
|
24
vendor/github.com/gofrs/flock/.gitignore
generated
vendored
Normal file
24
vendor/github.com/gofrs/flock/.gitignore
generated
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||||
|
*.o
|
||||||
|
*.a
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Folders
|
||||||
|
_obj
|
||||||
|
_test
|
||||||
|
|
||||||
|
# Architecture specific extensions/prefixes
|
||||||
|
*.[568vq]
|
||||||
|
[568vq].out
|
||||||
|
|
||||||
|
*.cgo1.go
|
||||||
|
*.cgo2.c
|
||||||
|
_cgo_defun.c
|
||||||
|
_cgo_gotypes.go
|
||||||
|
_cgo_export.*
|
||||||
|
|
||||||
|
_testmain.go
|
||||||
|
|
||||||
|
*.exe
|
||||||
|
*.test
|
||||||
|
*.prof
|
10
vendor/github.com/gofrs/flock/.travis.yml
generated
vendored
Normal file
10
vendor/github.com/gofrs/flock/.travis.yml
generated
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
language: go
|
||||||
|
go:
|
||||||
|
- 1.10.x
|
||||||
|
- 1.11.x
|
||||||
|
script: go test -v -check.vv -race ./...
|
||||||
|
sudo: false
|
||||||
|
notifications:
|
||||||
|
email:
|
||||||
|
on_success: never
|
||||||
|
on_failure: always
|
27
vendor/github.com/gofrs/flock/LICENSE
generated
vendored
Normal file
27
vendor/github.com/gofrs/flock/LICENSE
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
Copyright (c) 2015, Tim Heckman
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
* Neither the name of linode-netint nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
41
vendor/github.com/gofrs/flock/README.md
generated
vendored
Normal file
41
vendor/github.com/gofrs/flock/README.md
generated
vendored
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
# flock
|
||||||
|
[](https://travis-ci.org/gofrs/flock)
|
||||||
|
[](https://godoc.org/github.com/gofrs/flock)
|
||||||
|
[](https://github.com/gofrs/flock/blob/master/LICENSE)
|
||||||
|
[](https://goreportcard.com/report/github.com/gofrs/flock)
|
||||||
|
|
||||||
|
`flock` implements a thread-safe sync.Locker interface for file locking. It also
|
||||||
|
includes a non-blocking TryLock() function to allow locking without blocking execution.
|
||||||
|
|
||||||
|
## License
|
||||||
|
`flock` is released under the BSD 3-Clause License. See the `LICENSE` file for more details.
|
||||||
|
|
||||||
|
## Go Compatibility
|
||||||
|
This package makes use of the `context` package that was introduced in Go 1.7. As such, this
|
||||||
|
package has an implicit dependency on Go 1.7+.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
```
|
||||||
|
go get -u github.com/gofrs/flock
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
```Go
|
||||||
|
import "github.com/gofrs/flock"
|
||||||
|
|
||||||
|
fileLock := flock.New("/var/lock/go-lock.lock")
|
||||||
|
|
||||||
|
locked, err := fileLock.TryLock()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
// handle locking error
|
||||||
|
}
|
||||||
|
|
||||||
|
if locked {
|
||||||
|
// do work
|
||||||
|
fileLock.Unlock()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
For more detailed usage information take a look at the package API docs on
|
||||||
|
[GoDoc](https://godoc.org/github.com/gofrs/flock).
|
25
vendor/github.com/gofrs/flock/appveyor.yml
generated
vendored
Normal file
25
vendor/github.com/gofrs/flock/appveyor.yml
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
version: '{build}'
|
||||||
|
|
||||||
|
build: false
|
||||||
|
deploy: false
|
||||||
|
|
||||||
|
clone_folder: 'c:\gopath\src\github.com\gofrs\flock'
|
||||||
|
|
||||||
|
environment:
|
||||||
|
GOPATH: 'c:\gopath'
|
||||||
|
GOVERSION: '1.11'
|
||||||
|
|
||||||
|
init:
|
||||||
|
- git config --global core.autocrlf input
|
||||||
|
|
||||||
|
install:
|
||||||
|
- rmdir c:\go /s /q
|
||||||
|
- appveyor DownloadFile https://storage.googleapis.com/golang/go%GOVERSION%.windows-amd64.msi
|
||||||
|
- msiexec /i go%GOVERSION%.windows-amd64.msi /q
|
||||||
|
- set Path=c:\go\bin;c:\gopath\bin;%Path%
|
||||||
|
- go version
|
||||||
|
- go env
|
||||||
|
|
||||||
|
test_script:
|
||||||
|
- go get -t ./...
|
||||||
|
- go test -race -v ./...
|
127
vendor/github.com/gofrs/flock/flock.go
generated
vendored
Normal file
127
vendor/github.com/gofrs/flock/flock.go
generated
vendored
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
// Copyright 2015 Tim Heckman. All rights reserved.
|
||||||
|
// Use of this source code is governed by the BSD 3-Clause
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package flock implements a thread-safe interface for file locking.
|
||||||
|
// It also includes a non-blocking TryLock() function to allow locking
|
||||||
|
// without blocking execution.
|
||||||
|
//
|
||||||
|
// Package flock is released under the BSD 3-Clause License. See the LICENSE file
|
||||||
|
// for more details.
|
||||||
|
//
|
||||||
|
// While using this library, remember that the locking behaviors are not
|
||||||
|
// guaranteed to be the same on each platform. For example, some UNIX-like
|
||||||
|
// operating systems will transparently convert a shared lock to an exclusive
|
||||||
|
// lock. If you Unlock() the flock from a location where you believe that you
|
||||||
|
// have the shared lock, you may accidentally drop the exclusive lock.
|
||||||
|
package flock
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Flock is the struct type to handle file locking. All fields are unexported,
|
||||||
|
// with access to some of the fields provided by getter methods (Path() and Locked()).
|
||||||
|
type Flock struct {
|
||||||
|
path string
|
||||||
|
m sync.RWMutex
|
||||||
|
fh *os.File
|
||||||
|
l bool
|
||||||
|
r bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns a new instance of *Flock. The only parameter
|
||||||
|
// it takes is the path to the desired lockfile.
|
||||||
|
func New(path string) *Flock {
|
||||||
|
return &Flock{path: path}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFlock returns a new instance of *Flock. The only parameter
|
||||||
|
// it takes is the path to the desired lockfile.
|
||||||
|
//
|
||||||
|
// Deprecated: Use New instead.
|
||||||
|
func NewFlock(path string) *Flock {
|
||||||
|
return New(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close is equivalent to calling Unlock.
|
||||||
|
//
|
||||||
|
// This will release the lock and close the underlying file descriptor.
|
||||||
|
// It will not remove the file from disk, that's up to your application.
|
||||||
|
func (f *Flock) Close() error {
|
||||||
|
return f.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Path returns the path as provided in NewFlock().
|
||||||
|
func (f *Flock) Path() string {
|
||||||
|
return f.path
|
||||||
|
}
|
||||||
|
|
||||||
|
// Locked returns the lock state (locked: true, unlocked: false).
|
||||||
|
//
|
||||||
|
// Warning: by the time you use the returned value, the state may have changed.
|
||||||
|
func (f *Flock) Locked() bool {
|
||||||
|
f.m.RLock()
|
||||||
|
defer f.m.RUnlock()
|
||||||
|
return f.l
|
||||||
|
}
|
||||||
|
|
||||||
|
// RLocked returns the read lock state (locked: true, unlocked: false).
|
||||||
|
//
|
||||||
|
// Warning: by the time you use the returned value, the state may have changed.
|
||||||
|
func (f *Flock) RLocked() bool {
|
||||||
|
f.m.RLock()
|
||||||
|
defer f.m.RUnlock()
|
||||||
|
return f.r
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Flock) String() string {
|
||||||
|
return f.path
|
||||||
|
}
|
||||||
|
|
||||||
|
// TryLockContext repeatedly tries to take an exclusive lock until one of the
|
||||||
|
// conditions is met: TryLock succeeds, TryLock fails with error, or Context
|
||||||
|
// Done channel is closed.
|
||||||
|
func (f *Flock) TryLockContext(ctx context.Context, retryDelay time.Duration) (bool, error) {
|
||||||
|
return tryCtx(ctx, f.TryLock, retryDelay)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TryRLockContext repeatedly tries to take a shared lock until one of the
|
||||||
|
// conditions is met: TryRLock succeeds, TryRLock fails with error, or Context
|
||||||
|
// Done channel is closed.
|
||||||
|
func (f *Flock) TryRLockContext(ctx context.Context, retryDelay time.Duration) (bool, error) {
|
||||||
|
return tryCtx(ctx, f.TryRLock, retryDelay)
|
||||||
|
}
|
||||||
|
|
||||||
|
func tryCtx(ctx context.Context, fn func() (bool, error), retryDelay time.Duration) (bool, error) {
|
||||||
|
if ctx.Err() != nil {
|
||||||
|
return false, ctx.Err()
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
if ok, err := fn(); ok || err != nil {
|
||||||
|
return ok, err
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return false, ctx.Err()
|
||||||
|
case <-time.After(retryDelay):
|
||||||
|
// try again
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Flock) setFh() error {
|
||||||
|
// open a new os.File instance
|
||||||
|
// create it if it doesn't exist, and open the file read-only.
|
||||||
|
fh, err := os.OpenFile(f.path, os.O_CREATE|os.O_RDONLY, os.FileMode(0600))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the filehandle on the struct
|
||||||
|
f.fh = fh
|
||||||
|
return nil
|
||||||
|
}
|
195
vendor/github.com/gofrs/flock/flock_unix.go
generated
vendored
Normal file
195
vendor/github.com/gofrs/flock/flock_unix.go
generated
vendored
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
// Copyright 2015 Tim Heckman. All rights reserved.
|
||||||
|
// Use of this source code is governed by the BSD 3-Clause
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build !windows
|
||||||
|
|
||||||
|
package flock
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Lock is a blocking call to try and take an exclusive file lock. It will wait
|
||||||
|
// until it is able to obtain the exclusive file lock. It's recommended that
|
||||||
|
// TryLock() be used over this function. This function may block the ability to
|
||||||
|
// query the current Locked() or RLocked() status due to a RW-mutex lock.
|
||||||
|
//
|
||||||
|
// If we are already exclusive-locked, this function short-circuits and returns
|
||||||
|
// immediately assuming it can take the mutex lock.
|
||||||
|
//
|
||||||
|
// If the *Flock has a shared lock (RLock), this may transparently replace the
|
||||||
|
// shared lock with an exclusive lock on some UNIX-like operating systems. Be
|
||||||
|
// careful when using exclusive locks in conjunction with shared locks
|
||||||
|
// (RLock()), because calling Unlock() may accidentally release the exclusive
|
||||||
|
// lock that was once a shared lock.
|
||||||
|
func (f *Flock) Lock() error {
|
||||||
|
return f.lock(&f.l, syscall.LOCK_EX)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RLock is a blocking call to try and take a shared file lock. It will wait
|
||||||
|
// until it is able to obtain the shared file lock. It's recommended that
|
||||||
|
// TryRLock() be used over this function. This function may block the ability to
|
||||||
|
// query the current Locked() or RLocked() status due to a RW-mutex lock.
|
||||||
|
//
|
||||||
|
// If we are already shared-locked, this function short-circuits and returns
|
||||||
|
// immediately assuming it can take the mutex lock.
|
||||||
|
func (f *Flock) RLock() error {
|
||||||
|
return f.lock(&f.r, syscall.LOCK_SH)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Flock) lock(locked *bool, flag int) error {
|
||||||
|
f.m.Lock()
|
||||||
|
defer f.m.Unlock()
|
||||||
|
|
||||||
|
if *locked {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.fh == nil {
|
||||||
|
if err := f.setFh(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := syscall.Flock(int(f.fh.Fd()), flag); err != nil {
|
||||||
|
shouldRetry, reopenErr := f.reopenFDOnError(err)
|
||||||
|
if reopenErr != nil {
|
||||||
|
return reopenErr
|
||||||
|
}
|
||||||
|
|
||||||
|
if !shouldRetry {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = syscall.Flock(int(f.fh.Fd()), flag); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*locked = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unlock is a function to unlock the file. This file takes a RW-mutex lock, so
|
||||||
|
// while it is running the Locked() and RLocked() functions will be blocked.
|
||||||
|
//
|
||||||
|
// This function short-circuits if we are unlocked already. If not, it calls
|
||||||
|
// syscall.LOCK_UN on the file and closes the file descriptor. It does not
|
||||||
|
// remove the file from disk. It's up to your application to do.
|
||||||
|
//
|
||||||
|
// Please note, if your shared lock became an exclusive lock this may
|
||||||
|
// unintentionally drop the exclusive lock if called by the consumer that
|
||||||
|
// believes they have a shared lock. Please see Lock() for more details.
|
||||||
|
func (f *Flock) Unlock() error {
|
||||||
|
f.m.Lock()
|
||||||
|
defer f.m.Unlock()
|
||||||
|
|
||||||
|
// if we aren't locked or if the lockfile instance is nil
|
||||||
|
// just return a nil error because we are unlocked
|
||||||
|
if (!f.l && !f.r) || f.fh == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// mark the file as unlocked
|
||||||
|
if err := syscall.Flock(int(f.fh.Fd()), syscall.LOCK_UN); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
f.fh.Close()
|
||||||
|
|
||||||
|
f.l = false
|
||||||
|
f.r = false
|
||||||
|
f.fh = nil
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TryLock is the preferred function for taking an exclusive file lock. This
|
||||||
|
// function takes an RW-mutex lock before it tries to lock the file, so there is
|
||||||
|
// the possibility that this function may block for a short time if another
|
||||||
|
// goroutine is trying to take any action.
|
||||||
|
//
|
||||||
|
// The actual file lock is non-blocking. If we are unable to get the exclusive
|
||||||
|
// file lock, the function will return false instead of waiting for the lock. If
|
||||||
|
// we get the lock, we also set the *Flock instance as being exclusive-locked.
|
||||||
|
func (f *Flock) TryLock() (bool, error) {
|
||||||
|
return f.try(&f.l, syscall.LOCK_EX)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TryRLock is the preferred function for taking a shared file lock. This
|
||||||
|
// function takes an RW-mutex lock before it tries to lock the file, so there is
|
||||||
|
// the possibility that this function may block for a short time if another
|
||||||
|
// goroutine is trying to take any action.
|
||||||
|
//
|
||||||
|
// The actual file lock is non-blocking. If we are unable to get the shared file
|
||||||
|
// lock, the function will return false instead of waiting for the lock. If we
|
||||||
|
// get the lock, we also set the *Flock instance as being share-locked.
|
||||||
|
func (f *Flock) TryRLock() (bool, error) {
|
||||||
|
return f.try(&f.r, syscall.LOCK_SH)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Flock) try(locked *bool, flag int) (bool, error) {
|
||||||
|
f.m.Lock()
|
||||||
|
defer f.m.Unlock()
|
||||||
|
|
||||||
|
if *locked {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.fh == nil {
|
||||||
|
if err := f.setFh(); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var retried bool
|
||||||
|
retry:
|
||||||
|
err := syscall.Flock(int(f.fh.Fd()), flag|syscall.LOCK_NB)
|
||||||
|
|
||||||
|
switch err {
|
||||||
|
case syscall.EWOULDBLOCK:
|
||||||
|
return false, nil
|
||||||
|
case nil:
|
||||||
|
*locked = true
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
if !retried {
|
||||||
|
if shouldRetry, reopenErr := f.reopenFDOnError(err); reopenErr != nil {
|
||||||
|
return false, reopenErr
|
||||||
|
} else if shouldRetry {
|
||||||
|
retried = true
|
||||||
|
goto retry
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// reopenFDOnError determines whether we should reopen the file handle
|
||||||
|
// in readwrite mode and try again. This comes from util-linux/sys-utils/flock.c:
|
||||||
|
// Since Linux 3.4 (commit 55725513)
|
||||||
|
// Probably NFSv4 where flock() is emulated by fcntl().
|
||||||
|
func (f *Flock) reopenFDOnError(err error) (bool, error) {
|
||||||
|
if err != syscall.EIO && err != syscall.EBADF {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
if st, err := f.fh.Stat(); err == nil {
|
||||||
|
// if the file is able to be read and written
|
||||||
|
if st.Mode()&0600 == 0600 {
|
||||||
|
f.fh.Close()
|
||||||
|
f.fh = nil
|
||||||
|
|
||||||
|
// reopen in read-write mode and set the filehandle
|
||||||
|
fh, err := os.OpenFile(f.path, os.O_CREATE|os.O_RDWR, os.FileMode(0600))
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
f.fh = fh
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, nil
|
||||||
|
}
|
76
vendor/github.com/gofrs/flock/flock_winapi.go
generated
vendored
Normal file
76
vendor/github.com/gofrs/flock/flock_winapi.go
generated
vendored
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
// Copyright 2015 Tim Heckman. All rights reserved.
|
||||||
|
// Use of this source code is governed by the BSD 3-Clause
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package flock
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
kernel32, _ = syscall.LoadLibrary("kernel32.dll")
|
||||||
|
procLockFileEx, _ = syscall.GetProcAddress(kernel32, "LockFileEx")
|
||||||
|
procUnlockFileEx, _ = syscall.GetProcAddress(kernel32, "UnlockFileEx")
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
winLockfileFailImmediately = 0x00000001
|
||||||
|
winLockfileExclusiveLock = 0x00000002
|
||||||
|
winLockfileSharedLock = 0x00000000
|
||||||
|
)
|
||||||
|
|
||||||
|
// Use of 0x00000000 for the shared lock is a guess based on some the MS Windows
|
||||||
|
// `LockFileEX` docs, which document the `LOCKFILE_EXCLUSIVE_LOCK` flag as:
|
||||||
|
//
|
||||||
|
// > The function requests an exclusive lock. Otherwise, it requests a shared
|
||||||
|
// > lock.
|
||||||
|
//
|
||||||
|
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa365203(v=vs.85).aspx
|
||||||
|
|
||||||
|
func lockFileEx(handle syscall.Handle, flags uint32, reserved uint32, numberOfBytesToLockLow uint32, numberOfBytesToLockHigh uint32, offset *syscall.Overlapped) (bool, syscall.Errno) {
|
||||||
|
r1, _, errNo := syscall.Syscall6(
|
||||||
|
uintptr(procLockFileEx),
|
||||||
|
6,
|
||||||
|
uintptr(handle),
|
||||||
|
uintptr(flags),
|
||||||
|
uintptr(reserved),
|
||||||
|
uintptr(numberOfBytesToLockLow),
|
||||||
|
uintptr(numberOfBytesToLockHigh),
|
||||||
|
uintptr(unsafe.Pointer(offset)))
|
||||||
|
|
||||||
|
if r1 != 1 {
|
||||||
|
if errNo == 0 {
|
||||||
|
return false, syscall.EINVAL
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, errNo
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func unlockFileEx(handle syscall.Handle, reserved uint32, numberOfBytesToLockLow uint32, numberOfBytesToLockHigh uint32, offset *syscall.Overlapped) (bool, syscall.Errno) {
|
||||||
|
r1, _, errNo := syscall.Syscall6(
|
||||||
|
uintptr(procUnlockFileEx),
|
||||||
|
5,
|
||||||
|
uintptr(handle),
|
||||||
|
uintptr(reserved),
|
||||||
|
uintptr(numberOfBytesToLockLow),
|
||||||
|
uintptr(numberOfBytesToLockHigh),
|
||||||
|
uintptr(unsafe.Pointer(offset)),
|
||||||
|
0)
|
||||||
|
|
||||||
|
if r1 != 1 {
|
||||||
|
if errNo == 0 {
|
||||||
|
return false, syscall.EINVAL
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, errNo
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, 0
|
||||||
|
}
|
140
vendor/github.com/gofrs/flock/flock_windows.go
generated
vendored
Normal file
140
vendor/github.com/gofrs/flock/flock_windows.go
generated
vendored
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
// Copyright 2015 Tim Heckman. All rights reserved.
|
||||||
|
// Use of this source code is governed by the BSD 3-Clause
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package flock
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ErrorLockViolation is the error code returned from the Windows syscall when a
|
||||||
|
// lock would block and you ask to fail immediately.
|
||||||
|
const ErrorLockViolation syscall.Errno = 0x21 // 33
|
||||||
|
|
||||||
|
// Lock is a blocking call to try and take an exclusive file lock. It will wait
|
||||||
|
// until it is able to obtain the exclusive file lock. It's recommended that
|
||||||
|
// TryLock() be used over this function. This function may block the ability to
|
||||||
|
// query the current Locked() or RLocked() status due to a RW-mutex lock.
|
||||||
|
//
|
||||||
|
// If we are already locked, this function short-circuits and returns
|
||||||
|
// immediately assuming it can take the mutex lock.
|
||||||
|
func (f *Flock) Lock() error {
|
||||||
|
return f.lock(&f.l, winLockfileExclusiveLock)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RLock is a blocking call to try and take a shared file lock. It will wait
|
||||||
|
// until it is able to obtain the shared file lock. It's recommended that
|
||||||
|
// TryRLock() be used over this function. This function may block the ability to
|
||||||
|
// query the current Locked() or RLocked() status due to a RW-mutex lock.
|
||||||
|
//
|
||||||
|
// If we are already locked, this function short-circuits and returns
|
||||||
|
// immediately assuming it can take the mutex lock.
|
||||||
|
func (f *Flock) RLock() error {
|
||||||
|
return f.lock(&f.r, winLockfileSharedLock)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Flock) lock(locked *bool, flag uint32) error {
|
||||||
|
f.m.Lock()
|
||||||
|
defer f.m.Unlock()
|
||||||
|
|
||||||
|
if *locked {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.fh == nil {
|
||||||
|
if err := f.setFh(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, errNo := lockFileEx(syscall.Handle(f.fh.Fd()), flag, 0, 1, 0, &syscall.Overlapped{}); errNo > 0 {
|
||||||
|
return errNo
|
||||||
|
}
|
||||||
|
|
||||||
|
*locked = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unlock is a function to unlock the file. This file takes a RW-mutex lock, so
|
||||||
|
// while it is running the Locked() and RLocked() functions will be blocked.
|
||||||
|
//
|
||||||
|
// This function short-circuits if we are unlocked already. If not, it calls
|
||||||
|
// UnlockFileEx() on the file and closes the file descriptor. It does not remove
|
||||||
|
// the file from disk. It's up to your application to do.
|
||||||
|
func (f *Flock) Unlock() error {
|
||||||
|
f.m.Lock()
|
||||||
|
defer f.m.Unlock()
|
||||||
|
|
||||||
|
// if we aren't locked or if the lockfile instance is nil
|
||||||
|
// just return a nil error because we are unlocked
|
||||||
|
if (!f.l && !f.r) || f.fh == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// mark the file as unlocked
|
||||||
|
if _, errNo := unlockFileEx(syscall.Handle(f.fh.Fd()), 0, 1, 0, &syscall.Overlapped{}); errNo > 0 {
|
||||||
|
return errNo
|
||||||
|
}
|
||||||
|
|
||||||
|
f.fh.Close()
|
||||||
|
|
||||||
|
f.l = false
|
||||||
|
f.r = false
|
||||||
|
f.fh = nil
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TryLock is the preferred function for taking an exclusive file lock. This
|
||||||
|
// function does take a RW-mutex lock before it tries to lock the file, so there
|
||||||
|
// is the possibility that this function may block for a short time if another
|
||||||
|
// goroutine is trying to take any action.
|
||||||
|
//
|
||||||
|
// The actual file lock is non-blocking. If we are unable to get the exclusive
|
||||||
|
// file lock, the function will return false instead of waiting for the lock. If
|
||||||
|
// we get the lock, we also set the *Flock instance as being exclusive-locked.
|
||||||
|
func (f *Flock) TryLock() (bool, error) {
|
||||||
|
return f.try(&f.l, winLockfileExclusiveLock)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TryRLock is the preferred function for taking a shared file lock. This
|
||||||
|
// function does take a RW-mutex lock before it tries to lock the file, so there
|
||||||
|
// is the possibility that this function may block for a short time if another
|
||||||
|
// goroutine is trying to take any action.
|
||||||
|
//
|
||||||
|
// The actual file lock is non-blocking. If we are unable to get the shared file
|
||||||
|
// lock, the function will return false instead of waiting for the lock. If we
|
||||||
|
// get the lock, we also set the *Flock instance as being shared-locked.
|
||||||
|
func (f *Flock) TryRLock() (bool, error) {
|
||||||
|
return f.try(&f.r, winLockfileSharedLock)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Flock) try(locked *bool, flag uint32) (bool, error) {
|
||||||
|
f.m.Lock()
|
||||||
|
defer f.m.Unlock()
|
||||||
|
|
||||||
|
if *locked {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.fh == nil {
|
||||||
|
if err := f.setFh(); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_, errNo := lockFileEx(syscall.Handle(f.fh.Fd()), flag|winLockfileFailImmediately, 0, 1, 0, &syscall.Overlapped{})
|
||||||
|
|
||||||
|
if errNo > 0 {
|
||||||
|
if errNo == ErrorLockViolation || errNo == syscall.ERROR_IO_PENDING {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, errNo
|
||||||
|
}
|
||||||
|
|
||||||
|
*locked = true
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
2
vendor/modules.txt
vendored
2
vendor/modules.txt
vendored
@ -44,6 +44,8 @@ github.com/gobwas/glob/syntax/ast
|
|||||||
github.com/gobwas/glob/syntax/lexer
|
github.com/gobwas/glob/syntax/lexer
|
||||||
github.com/gobwas/glob/util/runes
|
github.com/gobwas/glob/util/runes
|
||||||
github.com/gobwas/glob/util/strings
|
github.com/gobwas/glob/util/strings
|
||||||
|
# github.com/gofrs/flock v0.0.0-20190320160742-5135e617513b
|
||||||
|
github.com/gofrs/flock
|
||||||
# github.com/gogo/protobuf v1.2.1
|
# github.com/gogo/protobuf v1.2.1
|
||||||
github.com/gogo/protobuf/proto
|
github.com/gogo/protobuf/proto
|
||||||
# github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2
|
# github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2
|
||||||
|
Loading…
x
Reference in New Issue
Block a user