Documentation
¶
Overview ¶
Package flock implements a simple interface to flock(2) and fcntl(2) open file description locks on Linux.
Lock Semantics ¶
These locks are linked to file descriptors not processes. That means a lock can be inherited by child processes. It can be sent to unrelated processes via sendmsg(2) or similar. The file descriptor duplicated via dup(2) similarly inherits the lock. In all these cases, the lock is then shared by all file descriptors. If a lock is held this way by a number of file descriptors, it is released either when ANY one calls *Lock.Unlock or *Lock.UnlockRange (or the corresponding syscalls) or ALL of the file descriptors are closed.
These locks are advisory locks. They do not prevent any operation on the related files. The locks implemented by *Lock.LockEx, *Lock.LockSh and *Lock.Unlock represent exactly one lock per file. They are based on flock(2) and are somewhat portable across POSIX OS.
Range locks as provided by *Lock.LockRangeEx, *Lock.LockRangeSh and *Lock.UnlockRange are based on fcntl(2) and specific to Linux. They allow multiple independent locks on the same file descriptor.
fcntl(2) implements similar process based locks, see F_SETLK, F_SETLKW, and F_GETLK on the man page. These locks are NOT implemented by this package because their semantic is different.
Range Locks ¶
Range locks are good if you want to tie multiple locks to the same file. Every file position can be an independent lock. The lock range is not related to the file size. The file can have a size of 0 while you lock the range from 100 to 200. If you just need a single lock file, use flock(2) based lock.
Blocking ¶
This package has Lock... functions and TryLock... versions. They behave the same except that the Lock version waits until the lock becomes available while TryLock returns an error in this case.
In most cases, you likely don't want to use the blocking version in a Go program.
There is no context.Context based version or any other way to wait for a lock with a timeout. The only reliable way to implement that is by polling.
Ownership ¶
A Lock object might or might not OWN its file descriptor. If the object owns it, then the file descriptor is closed when *Lock.Close is called. Otherwise, the owner of the descriptor is responsible for closing it. See *Lock.File and WithFd for more information.
As a general rule, the Lock object owns the file descriptor if it has opened it.
Thread safety ¶
The internal data structure is NOT protected by any means.
Index ¶
- Constants
- Variables
- type Lock
- func (l *Lock) Close() error
- func (l *Lock) Fd() int
- func (l *Lock) File() *os.File
- func (l *Lock) LockEx() error
- func (l *Lock) LockRangeEx(start, len int64, whence ...int16) error
- func (l *Lock) LockRangeSh(start, len int64, whence ...int16) error
- func (l *Lock) LockSh() error
- func (l *Lock) Open() error
- func (l *Lock) OwningFd() bool
- func (l *Lock) Path() string
- func (l *Lock) TryLockEx() error
- func (l *Lock) TryLockRangeEx(start, len int64, whence ...int16) error
- func (l *Lock) TryLockRangeSh(start, len int64, whence ...int16) error
- func (l *Lock) TryLockSh() error
- func (l *Lock) Unlock() error
- func (l *Lock) UnlockRange(start, len int64, whence ...int16) error
- type Option
Constants ¶
const SEEK_CUR int16 = unix.SEEK_CUR
const SEEK_END int16 = unix.SEEK_END
const SEEK_SET int16 = unix.SEEK_SET
SEEK_SET, SEEK_CUR and SEEK_END represent the same values as used in Linux' lseek(2) syscall. They are used as parameters for range locks like *Lock.TryLockRangeEx.
Variables ¶
var ErrAlreadyOpen error = errors.New("file is already open")
ErrAlreadyOpen is returned by *Lock.Open if the underlying file descriptor is deemed to be open. There is no syscall performed to actually check if the file descriptor is already open. Just if the stored file descriptor number is negative, the error is triggered.
var ErrUnsupported error = errors.New("range locks not supported by OS")
Functions ¶
This section is empty.
Types ¶
type Lock ¶
type Lock struct {
// contains filtered or unexported fields
}
A Lock represents the lock object.
func New ¶
New creates the Lock object. At this time the file is not opened, yet. The file is only opened (unless an open file descriptor is passed), when either *Lock.Open is called or any of the Lock functions is called for the first time.
Example:
lck := flock.New(
flock.WithPath("/my/lock/file"),
flock.WithCreate(unix.S_IRUSR, unix.S_IWUSR),
flock.WithRdWr(),
flock.WithCloseOnExec(),
)
Note, if you use WithFd to pass an open file descriptor, that file descriptor will not be closed by *Lock.Close.
It is cheap to create these objects. No syscall involved.
func (*Lock) Close ¶
Close closes the underlying file descriptor if it is owned by the Lock object. It is not an error to close an already closed object. A subsequent *Lock.Open or any of the locking functions will re-open the file according to the parameters passed to New.
Note, Close does NOT unlock anything prior to closing the file. So, if the file descriptor is still open as in has been inherited by a child process or dup(2)ed or similar, the corresponding locks are still being held.
func (*Lock) Fd ¶
Fd returns the underlying file descriptor number. If the file is closed, -1 is returned.
func (*Lock) File ¶
File returns an *os.File object representing the underlying file. Calling this function disowns the file descriptor. After that, the *os.File object is responsible for closing it.
func (*Lock) LockEx ¶
LockEx exclusively locks the file using flock(2). If the file is not yet opened it is opened automatically. The returned error represents either an error in flock(2) itself or a failed open(2). On success nil is returned.
Note, this is a blocking syscall. If the lock is not available, it will block the calling GO routine. There is no good way to interrupt that syscall.
func (*Lock) LockSh ¶
LockSh represents a shared lock on the file using flock(2). If the file is not yet opened it is opened automatically. The returned error represents either an error in flock(2) itself or a failed open(2). On success nil is returned.
Note, this is a blocking syscall. If the lock is not available, it will block the calling GO routine. There is no good way to interrupt that syscall.
func (*Lock) Open ¶
Open opens the underlying file. If the file is deemed to be already open, ErrAlreadyOpen is returned. Otherwise, the underlying operating system error is returned. On success, nil is returned.
This function is called automatically when a locking function is called and the file is not yet open.
func (*Lock) OwningFd ¶
OwningFd can be used to determine if *Lock.Close will close the underlying file descriptor.
func (*Lock) TryLockEx ¶
TryLockEx is the non-blocking version of *Lock.LockEx. If the lock cannot be acquired unix.EWOULDBLOCK is returned.
func (*Lock) TryLockRangeEx ¶
func (*Lock) TryLockRangeSh ¶
func (*Lock) TryLockSh ¶
TryLockSh is the non-blocking version of *Lock.LockSh. If the lock cannot be acquired unix.EWOULDBLOCK is returned.
type Option ¶
type Option func(*opts)
An Option represents an option passed to the constructor New. All options are functions starting with With...
func WithCloseOnExec ¶
func WithCloseOnExec() Option
WithCloseOnExec corresponds to O_CLOEXEC in open(2).
func WithCreate ¶
WithCreate corresponds to O_CREAT in open(2). The parameters are ORed together to build the file mode. If completely omitted, the default file permissions are 0o600 or unix.S_IRUSR + unix.S_IWUSR or "rw-------".
The example below sets the file mode to 0o603 or "rw-----wx" (which does not make much sense, though)
Example:
lck := flock.New(
flock.WithCreate(unix.S_IRUSR, unix.S_IWUSR, 3),
...
)
func WithFd ¶
WithFd allows you to pass an open file descriptor to the constructor New. This can be combined with WithPath. However, the path in this case will never be used for anything. If a file descriptor is passed this way, it is not owned by the Lock object. So, *Lock.Close will not close the file. All the modifiers for opening the file, will also be ignored, WithRdWr, WithTruncate, ...
Passing a negative parameter, which is an invalid file descriptor, is ignored. *Lock.Fd will return -1.
func WithPath ¶
WithPath passes the name of the file, see New. This is optional. However, if no path is passed, a file descriptor number needs to be provided, see WithFd.
func WithoutIntrRetry ¶
func WithoutIntrRetry() Option
WithoutIntrRetry turns off the internal retry mechanism if the syscall comes back with EINTR. Mainly, this applies to the blocking syscalls.