mirror of
https://github.com/containers/skopeo.git
synced 2025-09-28 21:46:48 +00:00
fix(deps): update module github.com/containers/storage to v1.45.0
Signed-off-by: Renovate Bot <bot@renovateapp.com>
This commit is contained in:
63
vendor/github.com/containers/storage/pkg/lockfile/lockfile.go
generated
vendored
63
vendor/github.com/containers/storage/pkg/lockfile/lockfile.go
generated
vendored
@@ -10,6 +10,8 @@ import (
|
||||
// A Locker represents a file lock where the file is used to cache an
|
||||
// identifier of the last party that made changes to whatever's being protected
|
||||
// by the lock.
|
||||
//
|
||||
// Deprecated: Refer directly to *LockFile, the provided implementation, instead.
|
||||
type Locker interface {
|
||||
// Acquire a writer lock.
|
||||
// The default unix implementation panics if:
|
||||
@@ -28,10 +30,13 @@ type Locker interface {
|
||||
|
||||
// Touch records, for others sharing the lock, that the caller was the
|
||||
// last writer. It should only be called with the lock held.
|
||||
//
|
||||
// Deprecated: Use *LockFile.RecordWrite.
|
||||
Touch() error
|
||||
|
||||
// Modified() checks if the most recent writer was a party other than the
|
||||
// last recorded writer. It should only be called with the lock held.
|
||||
// Deprecated: Use *LockFile.ModifiedSince.
|
||||
Modified() (bool, error)
|
||||
|
||||
// TouchedSince() checks if the most recent writer modified the file (likely using Touch()) after the specified time.
|
||||
@@ -44,64 +49,82 @@ type Locker interface {
|
||||
// It might do nothing at all, or it may panic if the caller is not the owner of this lock.
|
||||
AssertLocked()
|
||||
|
||||
// AssertLocked() can be used by callers that _know_ that they hold the lock locked for writing, for sanity checking.
|
||||
// AssertLockedForWriting() can be used by callers that _know_ that they hold the lock locked for writing, for sanity checking.
|
||||
// It might do nothing at all, or it may panic if the caller is not the owner of this lock for writing.
|
||||
AssertLockedForWriting()
|
||||
}
|
||||
|
||||
var (
|
||||
lockfiles map[string]Locker
|
||||
lockfilesLock sync.Mutex
|
||||
lockFiles map[string]*LockFile
|
||||
lockFilesLock sync.Mutex
|
||||
)
|
||||
|
||||
// GetLockFile opens a read-write lock file, creating it if necessary. The
|
||||
// *LockFile object may already be locked if the path has already been requested
|
||||
// by the current process.
|
||||
func GetLockFile(path string) (*LockFile, error) {
|
||||
return getLockfile(path, false)
|
||||
}
|
||||
|
||||
// GetLockfile opens a read-write lock file, creating it if necessary. The
|
||||
// Locker object may already be locked if the path has already been requested
|
||||
// by the current process.
|
||||
//
|
||||
// Deprecated: Use GetLockFile
|
||||
func GetLockfile(path string) (Locker, error) {
|
||||
return getLockfile(path, false)
|
||||
return GetLockFile(path)
|
||||
}
|
||||
|
||||
// GetROLockFile opens a read-only lock file, creating it if necessary. The
|
||||
// *LockFile object may already be locked if the path has already been requested
|
||||
// by the current process.
|
||||
func GetROLockFile(path string) (*LockFile, error) {
|
||||
return getLockfile(path, true)
|
||||
}
|
||||
|
||||
// GetROLockfile opens a read-only lock file, creating it if necessary. The
|
||||
// Locker object may already be locked if the path has already been requested
|
||||
// by the current process.
|
||||
//
|
||||
// Deprecated: Use GetROLockFile
|
||||
func GetROLockfile(path string) (Locker, error) {
|
||||
return getLockfile(path, true)
|
||||
return GetROLockFile(path)
|
||||
}
|
||||
|
||||
// getLockfile returns a Locker object, possibly (depending on the platform)
|
||||
// getLockFile returns a *LockFile object, possibly (depending on the platform)
|
||||
// working inter-process, and associated with the specified path.
|
||||
//
|
||||
// If ro, the lock is a read-write lock and the returned Locker should correspond to the
|
||||
// If ro, the lock is a read-write lock and the returned *LockFile should correspond to the
|
||||
// “lock for reading” (shared) operation; otherwise, the lock is either an exclusive lock,
|
||||
// or a read-write lock and Locker should correspond to the “lock for writing” (exclusive) operation.
|
||||
// or a read-write lock and *LockFile should correspond to the “lock for writing” (exclusive) operation.
|
||||
//
|
||||
// WARNING:
|
||||
// - The lock may or MAY NOT be inter-process.
|
||||
// - There may or MAY NOT be an actual object on the filesystem created for the specified path.
|
||||
// - Even if ro, the lock MAY be exclusive.
|
||||
func getLockfile(path string, ro bool) (Locker, error) {
|
||||
lockfilesLock.Lock()
|
||||
defer lockfilesLock.Unlock()
|
||||
if lockfiles == nil {
|
||||
lockfiles = make(map[string]Locker)
|
||||
func getLockfile(path string, ro bool) (*LockFile, error) {
|
||||
lockFilesLock.Lock()
|
||||
defer lockFilesLock.Unlock()
|
||||
if lockFiles == nil {
|
||||
lockFiles = make(map[string]*LockFile)
|
||||
}
|
||||
cleanPath, err := filepath.Abs(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ensuring that path %q is an absolute path: %w", path, err)
|
||||
}
|
||||
if locker, ok := lockfiles[cleanPath]; ok {
|
||||
if ro && locker.IsReadWrite() {
|
||||
if lockFile, ok := lockFiles[cleanPath]; ok {
|
||||
if ro && lockFile.IsReadWrite() {
|
||||
return nil, fmt.Errorf("lock %q is not a read-only lock", cleanPath)
|
||||
}
|
||||
if !ro && !locker.IsReadWrite() {
|
||||
if !ro && !lockFile.IsReadWrite() {
|
||||
return nil, fmt.Errorf("lock %q is not a read-write lock", cleanPath)
|
||||
}
|
||||
return locker, nil
|
||||
return lockFile, nil
|
||||
}
|
||||
locker, err := createLockerForPath(cleanPath, ro) // platform-dependent locker
|
||||
lockFile, err := createLockFileForPath(cleanPath, ro) // platform-dependent LockFile
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lockfiles[cleanPath] = locker
|
||||
return locker, nil
|
||||
lockFiles[cleanPath] = lockFile
|
||||
return lockFile, nil
|
||||
}
|
||||
|
219
vendor/github.com/containers/storage/pkg/lockfile/lockfile_unix.go
generated
vendored
219
vendor/github.com/containers/storage/pkg/lockfile/lockfile_unix.go
generated
vendored
@@ -18,27 +18,48 @@ import (
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
type lockfile struct {
|
||||
// *LockFile represents a file lock where the file is used to cache an
|
||||
// identifier of the last party that made changes to whatever's being protected
|
||||
// by the lock.
|
||||
//
|
||||
// It MUST NOT be created manually. Use GetLockFile or GetROLockFile instead.
|
||||
type LockFile struct {
|
||||
// The following fields are only set when constructing *LockFile, and must never be modified afterwards.
|
||||
// They are safe to access without any other locking.
|
||||
file string
|
||||
ro bool
|
||||
|
||||
// rwMutex serializes concurrent reader-writer acquisitions in the same process space
|
||||
rwMutex *sync.RWMutex
|
||||
// stateMutex is used to synchronize concurrent accesses to the state below
|
||||
stateMutex *sync.Mutex
|
||||
counter int64
|
||||
file string
|
||||
fd uintptr
|
||||
lw []byte // "last writer"-unique value valid as of the last .Touch() or .Modified(), generated by newLastWriterID()
|
||||
lw LastWrite // A global value valid as of the last .Touch() or .Modified()
|
||||
locktype int16
|
||||
locked bool
|
||||
ro bool
|
||||
// The following fields are only modified on transitions between counter == 0 / counter != 0.
|
||||
// Thus, they can be safely accessed by users _that currently hold the LockFile_ without locking.
|
||||
// In other cases, they need to be protected using stateMutex.
|
||||
fd uintptr
|
||||
}
|
||||
|
||||
// LastWrite is an opaque identifier of the last write to some *LockFile.
|
||||
// It can be used by users of a *LockFile to determine if the lock indicates changes
|
||||
// since the last check.
|
||||
//
|
||||
// Never construct a LastWrite manually; only accept it from *LockFile methods, and pass it back.
|
||||
type LastWrite struct {
|
||||
// Never modify fields of a LastWrite object; it has value semantics.
|
||||
state []byte // Contents of the lock file.
|
||||
}
|
||||
|
||||
const lastWriterIDSize = 64 // This must be the same as len(stringid.GenerateRandomID)
|
||||
var lastWriterIDCounter uint64 // Private state for newLastWriterID
|
||||
|
||||
// newLastWriterID returns a new "last writer" ID.
|
||||
// newLastWrite returns a new "last write" ID.
|
||||
// The value must be different on every call, and also differ from values
|
||||
// generated by other processes.
|
||||
func newLastWriterID() []byte {
|
||||
func newLastWrite() LastWrite {
|
||||
// The ID is (PID, time, per-process counter, random)
|
||||
// PID + time represents both a unique process across reboots,
|
||||
// and a specific time within the process; the per-process counter
|
||||
@@ -60,7 +81,38 @@ func newLastWriterID() []byte {
|
||||
panic(err) // This shouldn't happen
|
||||
}
|
||||
|
||||
return res
|
||||
return LastWrite{
|
||||
state: res,
|
||||
}
|
||||
}
|
||||
|
||||
// newLastWriteFromData returns a LastWrite corresponding to data that came from a previous LastWrite.serialize
|
||||
func newLastWriteFromData(serialized []byte) LastWrite {
|
||||
if serialized == nil {
|
||||
panic("newLastWriteFromData with nil data")
|
||||
}
|
||||
return LastWrite{
|
||||
state: serialized,
|
||||
}
|
||||
}
|
||||
|
||||
// serialize returns bytes to write to the lock file to represent the specified write.
|
||||
func (lw LastWrite) serialize() []byte {
|
||||
if lw.state == nil {
|
||||
panic("LastWrite.serialize on an uninitialized object")
|
||||
}
|
||||
return lw.state
|
||||
}
|
||||
|
||||
// Equals returns true if lw matches other
|
||||
func (lw LastWrite) equals(other LastWrite) bool {
|
||||
if lw.state == nil {
|
||||
panic("LastWrite.equals on an uninitialized object")
|
||||
}
|
||||
if other.state == nil {
|
||||
panic("LastWrite.equals with an uninitialized counterparty")
|
||||
}
|
||||
return bytes.Equal(lw.state, other.state)
|
||||
}
|
||||
|
||||
// openLock opens the file at path and returns the corresponding file
|
||||
@@ -84,7 +136,7 @@ func openLock(path string, ro bool) (fd int, err error) {
|
||||
// the directory of the lockfile seems to be removed, try to create it
|
||||
if os.IsNotExist(err) {
|
||||
if err := os.MkdirAll(filepath.Dir(path), 0700); err != nil {
|
||||
return fd, fmt.Errorf("creating locker directory: %w", err)
|
||||
return fd, fmt.Errorf("creating lock file directory: %w", err)
|
||||
}
|
||||
|
||||
return openLock(path, ro)
|
||||
@@ -93,20 +145,20 @@ func openLock(path string, ro bool) (fd int, err error) {
|
||||
return fd, &os.PathError{Op: "open", Path: path, Err: err}
|
||||
}
|
||||
|
||||
// createLockerForPath returns a Locker object, possibly (depending on the platform)
|
||||
// createLockFileForPath returns new *LockFile object, possibly (depending on the platform)
|
||||
// working inter-process and associated with the specified path.
|
||||
//
|
||||
// This function will be called at most once for each path value within a single process.
|
||||
//
|
||||
// If ro, the lock is a read-write lock and the returned Locker should correspond to the
|
||||
// If ro, the lock is a read-write lock and the returned *LockFile should correspond to the
|
||||
// “lock for reading” (shared) operation; otherwise, the lock is either an exclusive lock,
|
||||
// or a read-write lock and Locker should correspond to the “lock for writing” (exclusive) operation.
|
||||
// or a read-write lock and *LockFile should correspond to the “lock for writing” (exclusive) operation.
|
||||
//
|
||||
// WARNING:
|
||||
// - The lock may or MAY NOT be inter-process.
|
||||
// - There may or MAY NOT be an actual object on the filesystem created for the specified path.
|
||||
// - Even if ro, the lock MAY be exclusive.
|
||||
func createLockerForPath(path string, ro bool) (Locker, error) {
|
||||
func createLockFileForPath(path string, ro bool) (*LockFile, error) {
|
||||
// Check if we can open the lock.
|
||||
fd, err := openLock(path, ro)
|
||||
if err != nil {
|
||||
@@ -118,19 +170,21 @@ func createLockerForPath(path string, ro bool) (Locker, error) {
|
||||
if ro {
|
||||
locktype = unix.F_RDLCK
|
||||
}
|
||||
return &lockfile{
|
||||
stateMutex: &sync.Mutex{},
|
||||
return &LockFile{
|
||||
file: path,
|
||||
ro: ro,
|
||||
|
||||
rwMutex: &sync.RWMutex{},
|
||||
file: path,
|
||||
lw: newLastWriterID(),
|
||||
stateMutex: &sync.Mutex{},
|
||||
lw: newLastWrite(), // For compatibility, the first call of .Modified() will always report a change.
|
||||
locktype: int16(locktype),
|
||||
locked: false,
|
||||
ro: ro}, nil
|
||||
}, nil
|
||||
}
|
||||
|
||||
// lock locks the lockfile via FCTNL(2) based on the specified type and
|
||||
// command.
|
||||
func (l *lockfile) lock(lType int16) {
|
||||
func (l *LockFile) lock(lType int16) {
|
||||
lk := unix.Flock_t{
|
||||
Type: lType,
|
||||
Whence: int16(unix.SEEK_SET),
|
||||
@@ -168,7 +222,7 @@ func (l *lockfile) lock(lType int16) {
|
||||
}
|
||||
|
||||
// Lock locks the lockfile as a writer. Panic if the lock is a read-only one.
|
||||
func (l *lockfile) Lock() {
|
||||
func (l *LockFile) Lock() {
|
||||
if l.ro {
|
||||
panic("can't take write lock on read-only lock file")
|
||||
} else {
|
||||
@@ -177,12 +231,12 @@ func (l *lockfile) Lock() {
|
||||
}
|
||||
|
||||
// LockRead locks the lockfile as a reader.
|
||||
func (l *lockfile) RLock() {
|
||||
func (l *LockFile) RLock() {
|
||||
l.lock(unix.F_RDLCK)
|
||||
}
|
||||
|
||||
// Unlock unlocks the lockfile.
|
||||
func (l *lockfile) Unlock() {
|
||||
func (l *LockFile) Unlock() {
|
||||
l.stateMutex.Lock()
|
||||
if !l.locked {
|
||||
// Panic when unlocking an unlocked lock. That's a violation
|
||||
@@ -213,7 +267,7 @@ func (l *lockfile) Unlock() {
|
||||
l.stateMutex.Unlock()
|
||||
}
|
||||
|
||||
func (l *lockfile) AssertLocked() {
|
||||
func (l *LockFile) AssertLocked() {
|
||||
// DO NOT provide a variant that returns the value of l.locked.
|
||||
//
|
||||
// If the caller does not hold the lock, l.locked might nevertheless be true because another goroutine does hold it, and
|
||||
@@ -230,7 +284,7 @@ func (l *lockfile) AssertLocked() {
|
||||
}
|
||||
}
|
||||
|
||||
func (l *lockfile) AssertLockedForWriting() {
|
||||
func (l *LockFile) AssertLockedForWriting() {
|
||||
// DO NOT provide a variant that returns the current lock state.
|
||||
//
|
||||
// The same caveats as for AssertLocked apply equally.
|
||||
@@ -242,53 +296,128 @@ func (l *lockfile) AssertLockedForWriting() {
|
||||
}
|
||||
}
|
||||
|
||||
// Touch updates the lock file with the UID of the user.
|
||||
func (l *lockfile) Touch() error {
|
||||
// GetLastWrite returns a LastWrite value corresponding to current state of the lock.
|
||||
// This is typically called before (_not after_) loading the state when initializing a consumer
|
||||
// of the data protected by the lock.
|
||||
// During the lifetime of the consumer, the consumer should usually call ModifiedSince instead.
|
||||
//
|
||||
// The caller must hold the lock (for reading or writing).
|
||||
func (l *LockFile) GetLastWrite() (LastWrite, error) {
|
||||
l.AssertLocked()
|
||||
contents := make([]byte, lastWriterIDSize)
|
||||
n, err := unix.Pread(int(l.fd), contents, 0)
|
||||
if err != nil {
|
||||
return LastWrite{}, err
|
||||
}
|
||||
// It is important to handle the partial read case, because
|
||||
// the initial size of the lock file is zero, which is a valid
|
||||
// state (no writes yet)
|
||||
contents = contents[:n]
|
||||
return newLastWriteFromData(contents), nil
|
||||
}
|
||||
|
||||
// RecordWrite updates the lock with a new LastWrite value, and returns the new value.
|
||||
//
|
||||
// If this function fails, the LastWriter value of the lock is indeterminate;
|
||||
// the caller should keep using the previously-recorded LastWrite value,
|
||||
// and possibly detecting its own modification as an external one:
|
||||
//
|
||||
// lw, err := state.lock.RecordWrite()
|
||||
// if err != nil { /* fail */ }
|
||||
// state.lastWrite = lw
|
||||
//
|
||||
// The caller must hold the lock for writing.
|
||||
func (l *LockFile) RecordWrite() (LastWrite, error) {
|
||||
l.AssertLockedForWriting()
|
||||
lw := newLastWrite()
|
||||
lockContents := lw.serialize()
|
||||
n, err := unix.Pwrite(int(l.fd), lockContents, 0)
|
||||
if err != nil {
|
||||
return LastWrite{}, err
|
||||
}
|
||||
if n != len(lockContents) {
|
||||
return LastWrite{}, unix.ENOSPC
|
||||
}
|
||||
return lw, nil
|
||||
}
|
||||
|
||||
// ModifiedSince checks if the lock has been changed since a provided LastWrite value,
|
||||
// and returns the one to record instead.
|
||||
//
|
||||
// If ModifiedSince reports no modification, the previous LastWrite value
|
||||
// is still valid and can continue to be used.
|
||||
//
|
||||
// If this function fails, the LastWriter value of the lock is indeterminate;
|
||||
// the caller should fail and keep using the previously-recorded LastWrite value,
|
||||
// so that it continues failing until the situation is resolved. Similarly,
|
||||
// it should only update the recorded LastWrite value after processing the update:
|
||||
//
|
||||
// lw2, modified, err := state.lock.ModifiedSince(state.lastWrite)
|
||||
// if err != nil { /* fail */ }
|
||||
// state.lastWrite = lw2
|
||||
// if modified {
|
||||
// if err := reload(); err != nil { /* fail */ }
|
||||
// state.lastWrite = lw2
|
||||
// }
|
||||
//
|
||||
// The caller must hold the lock (for reading or writing).
|
||||
func (l *LockFile) ModifiedSince(previous LastWrite) (LastWrite, bool, error) {
|
||||
l.AssertLocked()
|
||||
currentLW, err := l.GetLastWrite()
|
||||
if err != nil {
|
||||
return LastWrite{}, false, err
|
||||
}
|
||||
modified := !previous.equals(currentLW)
|
||||
return currentLW, modified, nil
|
||||
}
|
||||
|
||||
// Touch updates the lock file with to record that the current lock holder has modified the lock-protected data.
|
||||
//
|
||||
// Deprecated: Use *LockFile.RecordWrite.
|
||||
func (l *LockFile) Touch() error {
|
||||
lw, err := l.RecordWrite()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
l.stateMutex.Lock()
|
||||
if !l.locked || (l.locktype != unix.F_WRLCK) {
|
||||
panic("attempted to update last-writer in lockfile without the write lock")
|
||||
}
|
||||
defer l.stateMutex.Unlock()
|
||||
l.lw = newLastWriterID()
|
||||
n, err := unix.Pwrite(int(l.fd), l.lw, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if n != len(l.lw) {
|
||||
return unix.ENOSPC
|
||||
}
|
||||
l.lw = lw
|
||||
return nil
|
||||
}
|
||||
|
||||
// Modified indicates if the lockfile has been updated since the last time it
|
||||
// was loaded.
|
||||
func (l *lockfile) Modified() (bool, error) {
|
||||
// NOTE: Unlike ModifiedSince, this returns true the first time it is called on a *LockFile.
|
||||
// Callers cannot, in general, rely on this, because that might have happened for some other
|
||||
// owner of the same *LockFile who created it previously.
|
||||
//
|
||||
// Deprecated: Use *LockFile.ModifiedSince.
|
||||
func (l *LockFile) Modified() (bool, error) {
|
||||
l.stateMutex.Lock()
|
||||
if !l.locked {
|
||||
panic("attempted to check last-writer in lockfile without locking it first")
|
||||
}
|
||||
defer l.stateMutex.Unlock()
|
||||
currentLW := make([]byte, lastWriterIDSize)
|
||||
n, err := unix.Pread(int(l.fd), currentLW, 0)
|
||||
oldLW := l.lw
|
||||
// Note that this is called with stateMutex held; that’s fine because ModifiedSince doesn’t need to lock it.
|
||||
currentLW, modified, err := l.ModifiedSince(oldLW)
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
// It is important to handle the partial read case, because
|
||||
// the initial size of the lock file is zero, which is a valid
|
||||
// state (no writes yet)
|
||||
currentLW = currentLW[:n]
|
||||
oldLW := l.lw
|
||||
l.lw = currentLW
|
||||
return !bytes.Equal(currentLW, oldLW), nil
|
||||
return modified, nil
|
||||
}
|
||||
|
||||
// IsReadWriteLock indicates if the lock file is a read-write lock.
|
||||
func (l *lockfile) IsReadWrite() bool {
|
||||
func (l *LockFile) IsReadWrite() bool {
|
||||
return !l.ro
|
||||
}
|
||||
|
||||
// TouchedSince indicates if the lock file has been touched since the specified time
|
||||
func (l *lockfile) TouchedSince(when time.Time) bool {
|
||||
func (l *LockFile) TouchedSince(when time.Time) bool {
|
||||
st, err := system.Fstat(int(l.fd))
|
||||
if err != nil {
|
||||
return true
|
||||
|
96
vendor/github.com/containers/storage/pkg/lockfile/lockfile_windows.go
generated
vendored
96
vendor/github.com/containers/storage/pkg/lockfile/lockfile_windows.go
generated
vendored
@@ -9,45 +9,58 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// createLockerForPath returns a Locker object, possibly (depending on the platform)
|
||||
// createLockFileForPath returns a *LockFile object, possibly (depending on the platform)
|
||||
// working inter-process and associated with the specified path.
|
||||
//
|
||||
// This function will be called at most once for each path value within a single process.
|
||||
//
|
||||
// If ro, the lock is a read-write lock and the returned Locker should correspond to the
|
||||
// If ro, the lock is a read-write lock and the returned *LockFile should correspond to the
|
||||
// “lock for reading” (shared) operation; otherwise, the lock is either an exclusive lock,
|
||||
// or a read-write lock and Locker should correspond to the “lock for writing” (exclusive) operation.
|
||||
// or a read-write lock and *LockFile should correspond to the “lock for writing” (exclusive) operation.
|
||||
//
|
||||
// WARNING:
|
||||
// - The lock may or MAY NOT be inter-process.
|
||||
// - There may or MAY NOT be an actual object on the filesystem created for the specified path.
|
||||
// - Even if ro, the lock MAY be exclusive.
|
||||
func createLockerForPath(path string, ro bool) (Locker, error) {
|
||||
return &lockfile{locked: false}, nil
|
||||
func createLockFileForPath(path string, ro bool) (*LockFile, error) {
|
||||
return &LockFile{locked: false}, nil
|
||||
}
|
||||
|
||||
type lockfile struct {
|
||||
// *LockFile represents a file lock where the file is used to cache an
|
||||
// identifier of the last party that made changes to whatever's being protected
|
||||
// by the lock.
|
||||
//
|
||||
// It MUST NOT be created manually. Use GetLockFile or GetROLockFile instead.
|
||||
type LockFile struct {
|
||||
mu sync.Mutex
|
||||
file string
|
||||
locked bool
|
||||
}
|
||||
|
||||
func (l *lockfile) Lock() {
|
||||
// LastWrite is an opaque identifier of the last write to some *LockFile.
|
||||
// It can be used by users of a *LockFile to determine if the lock indicates changes
|
||||
// since the last check.
|
||||
// A default-initialized LastWrite never matches any last write, i.e. it always indicates changes.
|
||||
type LastWrite struct {
|
||||
// Nothing: The Windows “implementation” does not actually track writes.
|
||||
}
|
||||
|
||||
func (l *LockFile) Lock() {
|
||||
l.mu.Lock()
|
||||
l.locked = true
|
||||
}
|
||||
|
||||
func (l *lockfile) RLock() {
|
||||
func (l *LockFile) RLock() {
|
||||
l.mu.Lock()
|
||||
l.locked = true
|
||||
}
|
||||
|
||||
func (l *lockfile) Unlock() {
|
||||
func (l *LockFile) Unlock() {
|
||||
l.locked = false
|
||||
l.mu.Unlock()
|
||||
}
|
||||
|
||||
func (l *lockfile) AssertLocked() {
|
||||
func (l *LockFile) AssertLocked() {
|
||||
// DO NOT provide a variant that returns the value of l.locked.
|
||||
//
|
||||
// If the caller does not hold the lock, l.locked might nevertheless be true because another goroutine does hold it, and
|
||||
@@ -59,24 +72,77 @@ func (l *lockfile) AssertLocked() {
|
||||
}
|
||||
}
|
||||
|
||||
func (l *lockfile) AssertLockedForWriting() {
|
||||
func (l *LockFile) AssertLockedForWriting() {
|
||||
// DO NOT provide a variant that returns the current lock state.
|
||||
//
|
||||
// The same caveats as for AssertLocked apply equally.
|
||||
l.AssertLocked() // The current implementation does not distinguish between read and write locks.
|
||||
}
|
||||
|
||||
func (l *lockfile) Modified() (bool, error) {
|
||||
// GetLastWrite() returns a LastWrite value corresponding to current state of the lock.
|
||||
// This is typically called before (_not after_) loading the state when initializing a consumer
|
||||
// of the data protected by the lock.
|
||||
// During the lifetime of the consumer, the consumer should usually call ModifiedSince instead.
|
||||
//
|
||||
// The caller must hold the lock (for reading or writing) before this function is called.
|
||||
func (l *LockFile) GetLastWrite() (LastWrite, error) {
|
||||
l.AssertLocked()
|
||||
return LastWrite{}, nil
|
||||
}
|
||||
|
||||
// RecordWrite updates the lock with a new LastWrite value, and returns the new value.
|
||||
//
|
||||
// If this function fails, the LastWriter value of the lock is indeterminate;
|
||||
// the caller should keep using the previously-recorded LastWrite value,
|
||||
// and possibly detecting its own modification as an external one:
|
||||
//
|
||||
// lw, err := state.lock.RecordWrite()
|
||||
// if err != nil { /* fail */ }
|
||||
// state.lastWrite = lw
|
||||
//
|
||||
// The caller must hold the lock for writing.
|
||||
func (l *LockFile) RecordWrite() (LastWrite, error) {
|
||||
return LastWrite{}, nil
|
||||
}
|
||||
|
||||
// ModifiedSince checks if the lock has been changed since a provided LastWrite value,
|
||||
// and returns the one to record instead.
|
||||
//
|
||||
// If ModifiedSince reports no modification, the previous LastWrite value
|
||||
// is still valid and can continue to be used.
|
||||
//
|
||||
// If this function fails, the LastWriter value of the lock is indeterminate;
|
||||
// the caller should fail and keep using the previously-recorded LastWrite value,
|
||||
// so that it continues failing until the situation is resolved. Similarly,
|
||||
// it should only update the recorded LastWrite value after processing the update:
|
||||
//
|
||||
// lw2, modified, err := state.lock.ModifiedSince(state.lastWrite)
|
||||
// if err != nil { /* fail */ }
|
||||
// state.lastWrite = lw2
|
||||
// if modified {
|
||||
// if err := reload(); err != nil { /* fail */ }
|
||||
// state.lastWrite = lw2
|
||||
// }
|
||||
//
|
||||
// The caller must hold the lock (for reading or writing).
|
||||
func (l *LockFile) ModifiedSince(previous LastWrite) (LastWrite, bool, error) {
|
||||
return LastWrite{}, false, nil
|
||||
}
|
||||
|
||||
// Deprecated: Use *LockFile.ModifiedSince.
|
||||
func (l *LockFile) Modified() (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
func (l *lockfile) Touch() error {
|
||||
|
||||
// Deprecated: Use *LockFile.RecordWrite.
|
||||
func (l *LockFile) Touch() error {
|
||||
return nil
|
||||
}
|
||||
func (l *lockfile) IsReadWrite() bool {
|
||||
func (l *LockFile) IsReadWrite() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (l *lockfile) TouchedSince(when time.Time) bool {
|
||||
func (l *LockFile) TouchedSince(when time.Time) bool {
|
||||
stat, err := os.Stat(l.file)
|
||||
if err != nil {
|
||||
return true
|
||||
|
Reference in New Issue
Block a user