Help us shape the future of Go! Take the 2026 Go Developer Survey

flock

package module
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 12, 2026 License: Apache-2.0 Imports: 3 Imported by: 0

README

flock

A simple way to use flock and fcntl's OFD locks on linux

Installation

go get -u github.com/tfoertsch123/flock

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

View Source
const SEEK_CUR int16 = unix.SEEK_CUR
View Source
const SEEK_END int16 = unix.SEEK_END
View Source
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

View Source
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.

View Source
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

func New(opt ...Option) *Lock

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

func (l *Lock) Close() error

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

func (l *Lock) Fd() int

Fd returns the underlying file descriptor number. If the file is closed, -1 is returned.

func (*Lock) File

func (l *Lock) File() *os.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

func (l *Lock) LockEx() error

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) LockRangeEx

func (l *Lock) LockRangeEx(start, len int64, whence ...int16) error

func (*Lock) LockRangeSh

func (l *Lock) LockRangeSh(start, len int64, whence ...int16) error

func (*Lock) LockSh

func (l *Lock) LockSh() error

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

func (l *Lock) Open() error

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

func (l *Lock) OwningFd() bool

OwningFd can be used to determine if *Lock.Close will close the underlying file descriptor.

func (*Lock) Path

func (l *Lock) Path() string

Path returns the file path stored in the Lock object.

func (*Lock) TryLockEx

func (l *Lock) TryLockEx() error

TryLockEx is the non-blocking version of *Lock.LockEx. If the lock cannot be acquired unix.EWOULDBLOCK is returned.

func (*Lock) TryLockRangeEx

func (l *Lock) TryLockRangeEx(start, len int64, whence ...int16) error

func (*Lock) TryLockRangeSh

func (l *Lock) TryLockRangeSh(start, len int64, whence ...int16) error

func (*Lock) TryLockSh

func (l *Lock) TryLockSh() error

TryLockSh is the non-blocking version of *Lock.LockSh. If the lock cannot be acquired unix.EWOULDBLOCK is returned.

func (*Lock) Unlock

func (l *Lock) Unlock() error

Unlock releases the lock. It is not an error to unlock an already released lock.

If the underlying file is closed, it will not be opened and nil is returned.

Any error comes from the underlying flock(2) syscall.

func (*Lock) UnlockRange

func (l *Lock) UnlockRange(start, len int64, whence ...int16) error

type Option

type Option func(*opts)

An Option represents an option passed to the constructor New. All options are functions starting with With...

func WithAppend

func WithAppend() Option

WithAppend corresponds to O_APPEND in open(2).

func WithCloseOnExec

func WithCloseOnExec() Option

WithCloseOnExec corresponds to O_CLOEXEC in open(2).

func WithCreate

func WithCreate(perms ...uint32) Option

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

func WithFd(param int) Option

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 WithIsDir

func WithIsDir() Option

WithIsDir corresponds to O_DIRECTORY in open(2).

func WithPath

func WithPath(param string) Option

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 WithRdWr

func WithRdWr() Option

WithRdWr corresponds to O_RDWR in open(2).

func WithTruncate

func WithTruncate() Option

WithTruncate corresponds to O_TRUNC in open(2).

func WithWrOnly

func WithWrOnly() Option

WithWrOnly corresponds to O_WRONLY in open(2).

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL