file-lock
POSIX advisory record file locking for Rust, with automatic unlock on drop.
Repository Health
Technical Analysis
file-lock is a small Rust crate that wraps POSIX advisory record locks (via fcntl()) behind a safe, ergonomic API. It lets a process take a blocking or non-blocking read or write lock on a file, coordinating access across multiple processes using the same UNIX locking primitives system tools have relied on for decades.
The crate exposes a single FileLock type returned from FileLock::lock(), built from a FileOptions builder that mirrors std::fs::OpenOptions. Locks are released automatically when the FileLock value is dropped, or explicitly via unlock(), making it straightforward to guard access to config files, PID files, or any shared resource that needs cross-process coordination without pulling in a heavier concurrency dependency.
What You Get
- A
FileLocktype wrapping a lockedstd::fs::File, with the lock released automatically onDrop - A
FileOptionsbuilder mirroringstd::fs::OpenOptions(read, write, append, create, create_new, truncate) plus lock-specific writeable tracking - Blocking and non-blocking lock acquisition via a single
is_blockingflag passed toFileLock::lock() - A tiny C shim (
file_lock.c) compiled viabuild.rs/ccthat callsfcntl()directly for read (F_RDLCK) and write (F_WRLCK) advisory locks
Common Use Cases
- Guarding a PID file or lock file so only one instance of a daemon or CLI tool runs at a time
- Coordinating writes to a shared config or state file across multiple cooperating processes
- Implementing simple cross-process mutexes for build scripts, cron jobs, or worker pools that share a filesystem
- Serializing access to log files or append-only data files written by multiple processes
Under The Hood
Architecture
The crate is deliberately thin: src/lib.rs defines the public FileLock struct and its lock/unlock/Drop behavior, delegating the actual locking to two extern "C" functions, c_lock and c_unlock, implemented in src/file_lock.c and compiled into a static library by build.rs using the cc crate. The FileOptions builder lives in its own module (src/file_options.rs) and is consumed by FileLock::lock, which opens the file, reads the writeable flag off the options, and passes the raw file descriptor into the C shim via AsRawFd. There is no abstraction beyond this single FFI boundary — changing the C locking strategy (e.g. swapping fcntl() for flock()) would only touch file_lock.c, while changing the builder’s surface would only touch file_options.rs.
Tech Stack
Built for Rust 2021 edition with a single runtime dependency, libc 0.2.139, used for the c_int FFI type. The build pipeline compiles a small POSIX C file at build time via the cc 1.0.78 build-dependency, so any target must have a C toolchain (gcc) available. Dev-dependencies pull in nix 0.26.2 (process feature only) to exercise fork() in tests, simulating multi-process lock contention. There is no async runtime, framework, or database involved — it is a narrow systems-level binding intended to run on POSIX-compliant platforms.
Code Quality
The crate ships in-module tests under #[cfg(test)] in lib.rs, including an exhaustive combinatorial matrix (nested loops over already-exists, already-locked, already-writable, blocking, and writable flags) that forks a child process per case to verify locking and contention behavior end to end, plus a focused read/write-only test. Error handling is idiomatic Rust: fcntl() return codes map to std::io::Error::from_raw_os_error, and the public API returns Result rather than panicking. Naming follows standard Rust conventions and mirrors std::fs::OpenOptions intentionally for familiarity. CI is configured via a .travis.yml file (Travis CI, not GitHub Actions), which is dated tooling and, combined with the repo’s inactive activity status and unreviewed open issues, suggests the project receives limited ongoing maintenance despite its solid original test coverage.
API Design
The public surface is intentionally minimal: a single FileLock::lock(path, is_blocking, options) entry point and an optional explicit unlock(), with automatic unlocking on Drop removing an entire class of forgot-to-unlock bugs. FileOptions reuses std::fs::OpenOptions’ exact method names (read, write, append, create, create_new, truncate) so Rust developers already familiar with the standard library incur almost no learning curve. Doc comments throughout lib.rs include full, compilable usage examples for both lock and unlock, keeping the crate’s documentation self-contained even though it has no separate docs directory.