pub struct RunLock<Mode = Exclusive> { /* private fields */ }Expand description
RAII guard holding the run’s flock.
Released on drop. Acquired exclusively (RunLock::acquire) and held across
all writes for a single logical mutation, or shared (RunLock::acquire_shared)
for the duration of a reader’s multi-file scan so a concurrent reducer cannot
leave the reader with a half-updated projection set. A shared guard may hold
no lock at all when the run has no .lock file yet — see
RunLock::acquire_shared.
The Mode typestate (Exclusive / Shared) records which kind of lock
is held: only RunLock<Exclusive> exposes RunLock::witness, so a shared
reader can never forge the write capability a LockedRun represents.
Implementations§
Source§impl RunLock<Exclusive>
impl RunLock<Exclusive>
Sourcepub fn acquire(lock_path: &Path) -> Result<Self>
pub fn acquire(lock_path: &Path) -> Result<Self>
Acquire the exclusive lock on <run-dir>/.lock, creating the file if
needed. Blocks until the lock is available.
Best-effort symlink containment: a .lock that is a symlink is refused
(Error::SymlinkStateFile) so flock cannot be taken on a file
outside the run tree, which would silently break mutual exclusion. This
guards the lock file’s own final component; a symlinked run root is
caught downstream when the held critical section opens events.jsonl /
the projections (both re-guard the root before writing). See
reject_symlink for the check-then-open TOCTOU caveat.
Sourcepub fn witness(&self) -> LockedRun<'_>
pub fn witness(&self) -> LockedRun<'_>
Mint a LockedRun witness proving this exclusive guard holds the
run’s flock. The witness borrows self, so the borrow checker forbids
it from outliving the guard (and thus the lock). Use this for the
manually-held RunLock::acquire pattern — when the locked body needs
control flow (with_lock’s closure cannot express)
— then pass &witness to the unlocked append entry points.
Acquire a shared (LOCK_SH) lock on an existing <run-dir>/.lock,
blocking until no writer holds the exclusive lock. The mirror of
RunLock::acquire for the read side: many readers may hold the shared
lock at once, but the exclusive lock a reducer takes excludes them all,
so a multi-file read taken under this lock never observes the torn state
a mid-flight reducer would otherwise expose (design.md §4).
Unlike RunLock::acquire, this never creates the run directory or
the lock file — a reader must not bring run-tree state into existence. A
missing .lock means no writer has ever locked this run, so a lock-free
read is already coherent: the returned guard then holds nothing and drops
to a no-op. The same symlink containment as RunLock::acquire applies;
the file is opened read-only with O_NOFOLLOW.
Nesting is safe: a shared lock is compatible with other shared locks, so a read path that calls another read helper (each on its own descriptor) cannot deadlock against itself.
Convenience: run f with the shared lock held, releasing afterwards.
The read-side counterpart to RunLock::with_lock — but the closure
gets no LockedRun witness: a shared reader has no write
capability, so it can never reach a write-side entry point. (It also
takes the lock path directly rather than a RunPaths, since a reader
may run before the run dir is fully materialized.)