mirror of
				https://github.com/ergochat/ergo.git
				synced 2025-10-31 05:47:22 +01:00 
			
		
		
		
	optionally protect against multiple starts with flock (#1873)
* optionally protect against multiple starts with flock Fixes #1823 * use traditional .lock extension * move config key to top level
This commit is contained in:
		
							parent
							
								
									e112a78b9b
								
							
						
					
					
						commit
						ed75533cb1
					
				| @ -746,6 +746,12 @@ debug: | ||||
|     # set to `null`, "", leave blank, or omit to disable | ||||
|     # pprof-listener: "localhost:6060" | ||||
| 
 | ||||
| # lock file preventing multiple instances of Ergo from accidentally being | ||||
| # started at once. comment out or set to the empty string ("") to disable. | ||||
| # this path is relative to the working directory; you may want to use an | ||||
| # absolute path instead: | ||||
| lock-file: "ircd.lock" | ||||
| 
 | ||||
| # datastore configuration | ||||
| datastore: | ||||
|     # path to the datastore | ||||
|  | ||||
							
								
								
									
										2
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								go.mod
									
									
									
									
									
								
							| @ -25,6 +25,8 @@ require ( | ||||
| 	gopkg.in/yaml.v2 v2.4.0 | ||||
| ) | ||||
| 
 | ||||
| require github.com/gofrs/flock v0.8.1 | ||||
| 
 | ||||
| require ( | ||||
| 	github.com/tidwall/btree v0.6.1 // indirect | ||||
| 	github.com/tidwall/gjson v1.10.2 // indirect | ||||
|  | ||||
							
								
								
									
										2
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								go.sum
									
									
									
									
									
								
							| @ -21,6 +21,8 @@ github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfC | ||||
| github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= | ||||
| github.com/go-test/deep v1.0.6 h1:UHSEyLZUwX9Qoi99vVwvewiMC8mM2bf7XEM2nqvzEn8= | ||||
| github.com/go-test/deep v1.0.6/go.mod h1:QV8Hv/iy04NyLBxAdO9njL0iVPN1S4d/A3NVv1V36o8= | ||||
| github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= | ||||
| github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= | ||||
| github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= | ||||
| github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= | ||||
| github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= | ||||
|  | ||||
| @ -616,6 +616,8 @@ type Config struct { | ||||
| 
 | ||||
| 	languageManager *languages.Manager | ||||
| 
 | ||||
| 	LockFile string `yaml:"lock-file"` | ||||
| 
 | ||||
| 	Datastore struct { | ||||
| 		Path        string | ||||
| 		AutoUpgrade bool | ||||
|  | ||||
							
								
								
									
										24
									
								
								irc/flock/flock.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								irc/flock/flock.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,24 @@ | ||||
| //go:build !plan9 | ||||
| 
 | ||||
| package flock | ||||
| 
 | ||||
| import ( | ||||
| 	"errors" | ||||
| 
 | ||||
| 	"github.com/gofrs/flock" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	CouldntAcquire = errors.New("Couldn't acquire flock (is another Ergo running?)") | ||||
| ) | ||||
| 
 | ||||
| func TryAcquireFlock(path string) (fl Flocker, err error) { | ||||
| 	f := flock.New(path) | ||||
| 	success, err := f.TryLock() | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} else if !success { | ||||
| 		return nil, CouldntAcquire | ||||
| 	} | ||||
| 	return f, nil | ||||
| } | ||||
							
								
								
									
										14
									
								
								irc/flock/flock_iface.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								irc/flock/flock_iface.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,14 @@ | ||||
| package flock | ||||
| 
 | ||||
| // documentation for github.com/gofrs/flock incorrectly claims that | ||||
| // Flock implements sync.Locker; it does not because the Unlock method | ||||
| // has a return type (err). | ||||
| type Flocker interface { | ||||
| 	Unlock() error | ||||
| } | ||||
| 
 | ||||
| type noopFlocker struct{} | ||||
| 
 | ||||
| func (n *noopFlocker) Unlock() error { | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										7
									
								
								irc/flock/flock_plan9.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								irc/flock/flock_plan9.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | ||||
| //go:build plan9 | ||||
| 
 | ||||
| package flock | ||||
| 
 | ||||
| func TryAcquireFlock(path string) (fl Flocker, err error) { | ||||
| 	return &noopFlocker{}, nil | ||||
| } | ||||
| @ -25,6 +25,7 @@ import ( | ||||
| 	"github.com/ergochat/ergo/irc/caps" | ||||
| 	"github.com/ergochat/ergo/irc/connection_limits" | ||||
| 	"github.com/ergochat/ergo/irc/flatip" | ||||
| 	"github.com/ergochat/ergo/irc/flock" | ||||
| 	"github.com/ergochat/ergo/irc/history" | ||||
| 	"github.com/ergochat/ergo/irc/logger" | ||||
| 	"github.com/ergochat/ergo/irc/modes" | ||||
| @ -88,6 +89,7 @@ type Server struct { | ||||
| 	whoWas            WhoWasList | ||||
| 	stats             Stats | ||||
| 	semaphores        ServerSemaphores | ||||
| 	flock             flock.Flocker | ||||
| 	defcon            uint32 | ||||
| } | ||||
| 
 | ||||
| @ -585,6 +587,19 @@ func (server *Server) applyConfig(config *Config) (err error) { | ||||
| 
 | ||||
| 	server.logger.Info("server", "Using config file", server.configFilename) | ||||
| 
 | ||||
| 	if initial { | ||||
| 		if config.LockFile != "" { | ||||
| 			server.flock, err = flock.TryAcquireFlock(config.LockFile) | ||||
| 			if err != nil { | ||||
| 				return fmt.Errorf("failed to acquire flock on %s: %w", | ||||
| 					config.LockFile, err) | ||||
| 			} | ||||
| 		} | ||||
| 		// the lock is never released until quit; we need to save a pointer | ||||
| 		// to the (*flock.Flock) object so it doesn't get GC'ed, which would | ||||
| 		// close the file and surrender the lock | ||||
| 	} | ||||
| 
 | ||||
| 	// first, reload config sections for functionality implemented in subpackages: | ||||
| 	wasLoggingRawIO := !initial && server.logger.IsLoggingRawIO() | ||||
| 	err = server.logger.ApplyConfig(config.Logging) | ||||
|  | ||||
| @ -718,6 +718,12 @@ debug: | ||||
|     # set to `null`, "", leave blank, or omit to disable | ||||
|     # pprof-listener: "localhost:6060" | ||||
| 
 | ||||
| # lock file preventing multiple instances of Ergo from accidentally being | ||||
| # started at once. comment out or set to the empty string ("") to disable. | ||||
| # this path is relative to the working directory; you may want to use an | ||||
| # absolute path instead: | ||||
| lock-file: "ircd.lock" | ||||
| 
 | ||||
| # datastore configuration | ||||
| datastore: | ||||
|     # path to the datastore | ||||
|  | ||||
							
								
								
									
										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.14.x | ||||
|   - 1.15.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-2020, 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 gofrs 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.15' | ||||
| 
 | ||||
| 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 ./... | ||||
							
								
								
									
										144
									
								
								vendor/github.com/gofrs/flock/flock.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										144
									
								
								vendor/github.com/gofrs/flock/flock.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,144 @@ | ||||
| // 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" | ||||
| 	"runtime" | ||||
| 	"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. | ||||
| 	flags := os.O_CREATE | ||||
| 	if runtime.GOOS == "aix" { | ||||
| 		// AIX cannot preform write-lock (ie exclusive) on a | ||||
| 		// read-only file. | ||||
| 		flags |= os.O_RDWR | ||||
| 	} else { | ||||
| 		flags |= os.O_RDONLY | ||||
| 	} | ||||
| 	fh, err := os.OpenFile(f.path, flags, os.FileMode(0600)) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	// set the filehandle on the struct | ||||
| 	f.fh = fh | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // ensure the file handle is closed if no lock is held | ||||
| func (f *Flock) ensureFhState() { | ||||
| 	if !f.l && !f.r && f.fh != nil { | ||||
| 		f.fh.Close() | ||||
| 		f.fh = nil | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										281
									
								
								vendor/github.com/gofrs/flock/flock_aix.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										281
									
								
								vendor/github.com/gofrs/flock/flock_aix.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,281 @@ | ||||
| // Copyright 2019 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. | ||||
| 
 | ||||
| // Copyright 2018 The Go Authors. All rights reserved. | ||||
| // Use of this source code is governed by a BSD-style | ||||
| // license that can be found in the LICENSE file. | ||||
| 
 | ||||
| // This code implements the filelock API using POSIX 'fcntl' locks, which attach | ||||
| // to an (inode, process) pair rather than a file descriptor. To avoid unlocking | ||||
| // files prematurely when the same file is opened through different descriptors, | ||||
| // we allow only one read-lock at a time. | ||||
| // | ||||
| // This code is adapted from the Go package: | ||||
| // cmd/go/internal/lockedfile/internal/filelock | ||||
| 
 | ||||
| //+build aix | ||||
| 
 | ||||
| package flock | ||||
| 
 | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"io" | ||||
| 	"os" | ||||
| 	"sync" | ||||
| 	"syscall" | ||||
| 
 | ||||
| 	"golang.org/x/sys/unix" | ||||
| ) | ||||
| 
 | ||||
| type lockType int16 | ||||
| 
 | ||||
| const ( | ||||
| 	readLock  lockType = unix.F_RDLCK | ||||
| 	writeLock lockType = unix.F_WRLCK | ||||
| ) | ||||
| 
 | ||||
| type cmdType int | ||||
| 
 | ||||
| const ( | ||||
| 	tryLock  cmdType = unix.F_SETLK | ||||
| 	waitLock cmdType = unix.F_SETLKW | ||||
| ) | ||||
| 
 | ||||
| type inode = uint64 | ||||
| 
 | ||||
| type inodeLock struct { | ||||
| 	owner *Flock | ||||
| 	queue []<-chan *Flock | ||||
| } | ||||
| 
 | ||||
| var ( | ||||
| 	mu     sync.Mutex | ||||
| 	inodes = map[*Flock]inode{} | ||||
| 	locks  = map[inode]inodeLock{} | ||||
| ) | ||||
| 
 | ||||
| // 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, writeLock) | ||||
| } | ||||
| 
 | ||||
| // 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, readLock) | ||||
| } | ||||
| 
 | ||||
| func (f *Flock) lock(locked *bool, flag lockType) error { | ||||
| 	f.m.Lock() | ||||
| 	defer f.m.Unlock() | ||||
| 
 | ||||
| 	if *locked { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	if f.fh == nil { | ||||
| 		if err := f.setFh(); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		defer f.ensureFhState() | ||||
| 	} | ||||
| 
 | ||||
| 	if _, err := f.doLock(waitLock, flag, true); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	*locked = true | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (f *Flock) doLock(cmd cmdType, lt lockType, blocking bool) (bool, error) { | ||||
| 	// POSIX locks apply per inode and process, and the lock for an inode is | ||||
| 	// released when *any* descriptor for that inode is closed. So we need to | ||||
| 	// synchronize access to each inode internally, and must serialize lock and | ||||
| 	// unlock calls that refer to the same inode through different descriptors. | ||||
| 	fi, err := f.fh.Stat() | ||||
| 	if err != nil { | ||||
| 		return false, err | ||||
| 	} | ||||
| 	ino := inode(fi.Sys().(*syscall.Stat_t).Ino) | ||||
| 
 | ||||
| 	mu.Lock() | ||||
| 	if i, dup := inodes[f]; dup && i != ino { | ||||
| 		mu.Unlock() | ||||
| 		return false, &os.PathError{ | ||||
| 			Path: f.Path(), | ||||
| 			Err:  errors.New("inode for file changed since last Lock or RLock"), | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	inodes[f] = ino | ||||
| 
 | ||||
| 	var wait chan *Flock | ||||
| 	l := locks[ino] | ||||
| 	if l.owner == f { | ||||
| 		// This file already owns the lock, but the call may change its lock type. | ||||
| 	} else if l.owner == nil { | ||||
| 		// No owner: it's ours now. | ||||
| 		l.owner = f | ||||
| 	} else if !blocking { | ||||
| 		// Already owned: cannot take the lock. | ||||
| 		mu.Unlock() | ||||
| 		return false, nil | ||||
| 	} else { | ||||
| 		// Already owned: add a channel to wait on. | ||||
| 		wait = make(chan *Flock) | ||||
| 		l.queue = append(l.queue, wait) | ||||
| 	} | ||||
| 	locks[ino] = l | ||||
| 	mu.Unlock() | ||||
| 
 | ||||
| 	if wait != nil { | ||||
| 		wait <- f | ||||
| 	} | ||||
| 
 | ||||
| 	err = setlkw(f.fh.Fd(), cmd, lt) | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		f.doUnlock() | ||||
| 		if cmd == tryLock && err == unix.EACCES { | ||||
| 			return false, nil | ||||
| 		} | ||||
| 		return false, err | ||||
| 	} | ||||
| 
 | ||||
| 	return true, nil | ||||
| } | ||||
| 
 | ||||
| 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 | ||||
| 	} | ||||
| 
 | ||||
| 	if err := f.doUnlock(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	f.fh.Close() | ||||
| 
 | ||||
| 	f.l = false | ||||
| 	f.r = false | ||||
| 	f.fh = nil | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (f *Flock) doUnlock() (err error) { | ||||
| 	var owner *Flock | ||||
| 	mu.Lock() | ||||
| 	ino, ok := inodes[f] | ||||
| 	if ok { | ||||
| 		owner = locks[ino].owner | ||||
| 	} | ||||
| 	mu.Unlock() | ||||
| 
 | ||||
| 	if owner == f { | ||||
| 		err = setlkw(f.fh.Fd(), waitLock, unix.F_UNLCK) | ||||
| 	} | ||||
| 
 | ||||
| 	mu.Lock() | ||||
| 	l := locks[ino] | ||||
| 	if len(l.queue) == 0 { | ||||
| 		// No waiters: remove the map entry. | ||||
| 		delete(locks, ino) | ||||
| 	} else { | ||||
| 		// The first waiter is sending us their file now. | ||||
| 		// Receive it and update the queue. | ||||
| 		l.owner = <-l.queue[0] | ||||
| 		l.queue = l.queue[1:] | ||||
| 		locks[ino] = l | ||||
| 	} | ||||
| 	delete(inodes, f) | ||||
| 	mu.Unlock() | ||||
| 
 | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| // 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, writeLock) | ||||
| } | ||||
| 
 | ||||
| // 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, readLock) | ||||
| } | ||||
| 
 | ||||
| func (f *Flock) try(locked *bool, flag lockType) (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 | ||||
| 		} | ||||
| 		defer f.ensureFhState() | ||||
| 	} | ||||
| 
 | ||||
| 	haslock, err := f.doLock(tryLock, flag, false) | ||||
| 	if err != nil { | ||||
| 		return false, err | ||||
| 	} | ||||
| 
 | ||||
| 	*locked = haslock | ||||
| 	return haslock, nil | ||||
| } | ||||
| 
 | ||||
| // setlkw calls FcntlFlock with cmd for the entire file indicated by fd. | ||||
| func setlkw(fd uintptr, cmd cmdType, lt lockType) error { | ||||
| 	for { | ||||
| 		err := unix.FcntlFlock(fd, int(cmd), &unix.Flock_t{ | ||||
| 			Type:   int16(lt), | ||||
| 			Whence: io.SeekStart, | ||||
| 			Start:  0, | ||||
| 			Len:    0, // All bytes. | ||||
| 		}) | ||||
| 		if err != unix.EINTR { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										197
									
								
								vendor/github.com/gofrs/flock/flock_unix.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										197
									
								
								vendor/github.com/gofrs/flock/flock_unix.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,197 @@ | ||||
| // 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 !aix,!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 | ||||
| 		} | ||||
| 		defer f.ensureFhState() | ||||
| 	} | ||||
| 
 | ||||
| 	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 | ||||
| 		} | ||||
| 		defer f.ensureFhState() | ||||
| 	} | ||||
| 
 | ||||
| 	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 | ||||
| } | ||||
							
								
								
									
										142
									
								
								vendor/github.com/gofrs/flock/flock_windows.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										142
									
								
								vendor/github.com/gofrs/flock/flock_windows.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,142 @@ | ||||
| // 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 | ||||
| 		} | ||||
| 		defer f.ensureFhState() | ||||
| 	} | ||||
| 
 | ||||
| 	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 | ||||
| 		} | ||||
| 		defer f.ensureFhState() | ||||
| 	} | ||||
| 
 | ||||
| 	_, 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 | ||||
| } | ||||
							
								
								
									
										3
									
								
								vendor/modules.txt
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								vendor/modules.txt
									
									
									
									
										vendored
									
									
								
							| @ -27,6 +27,9 @@ github.com/ergochat/irc-go/ircutils | ||||
| github.com/go-sql-driver/mysql | ||||
| # github.com/go-test/deep v1.0.6 | ||||
| ## explicit; go 1.13 | ||||
| # github.com/gofrs/flock v0.8.1 | ||||
| ## explicit | ||||
| github.com/gofrs/flock | ||||
| # github.com/golang-jwt/jwt v3.2.2+incompatible | ||||
| ## explicit | ||||
| github.com/golang-jwt/jwt | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Shivaram Lingamneni
						Shivaram Lingamneni