Skip to main content

Module execution_lock

Module execution_lock 

Source
Expand description

Cross-process advisory lock enforcing INV-15’s single-owner-process invariant, held for the lifetime of a crate::DurableContext opened via LocalBackend::open_execution_exclusive.

Two processes can independently derive the same ExecutionId — the P1 agent-turn adapter keys it on (ConversationId, sqlite_path), so two CLI instances pointed at the same memory database and the same conversation always agree on the id (#6122). Without a lock, both processes race LocalBackend::open_execution’s plain SELECT-then-INSERT and both drive next_step from 0, corrupting the journal (ReplayDivergence/ReplayIntegrity on whichever process loses the race). ExecutionLock closes that race with a non-blocking, exclusive flock(2) on a lock file named after the execution’s UUID.

§Why not zeph_common::pidfile::PidLockGuard

PidLockGuard (the primitive backing zeph-core::daemon::PidGuard and zeph-scheduler::pidfile::PidFile) unlinks its lock file before its file descriptor closes (see its Drop impl). That ordering is safe for a pid file acquired once at daemon startup and released once at shutdown — vanishingly unlikely to race a concurrent acquirer — but is a real flock+unlink TOCTOU hazard for a lock acquired and released once per conversation turn under real contention (e.g. many CI agents sharing a testing database): a second process racing the unlink window can open(O_CREAT) a fresh inode at the just-unlinked path and flock it immediately, believing it holds the lock, while the first process’s descriptor — still open on the now-orphaned original inode — has not actually released yet. ExecutionLock instead mirrors zeph-session::log::SessionEventLog’s own AdvisoryLock: the lock file is never unlinked. It is a permanent sentinel, and the kernel releasing the flock when the holding process’s descriptors close (including on SIGKILL) is the only correctness signal — no unlink/re-create race, no PID-liveness polling needed.

Structs§

ExecutionLock
Holds the advisory lock for one ExecutionId while alive; releases it on drop.