Expand description
The two host locks: one that keeps a second agent from reconciling the same policies, and one that serialises runtime creation.
03-control-flows.md, flow 3.1: “A single-instance lock prevents two
agents on one host from reconciling the same policy.” Flow 2.4: the agent
“takes the host-wide allocation lock before creating each local runtime”.
07-security.md’s threat table names the single-instance lock as one of the
four controls on “API replay or a duplicate agent creates too many
runners”.
§Why an operating-system file lock, and not a PID file
The requirement that decides the mechanism is “released on crash rather
than leaking”. A PID file cannot do that: a process that is SIGKILLed, or
whose machine loses power, leaves the file behind, and every recovery
strategy built on top — is that PID still alive? was it reused? — is
guesswork that fails exactly when it matters. An operating-system file lock
is released by the kernel when the holding process ends, for any reason,
with no cooperation from the process and nothing left to clean up.
Two mechanisms, one behaviour:
- Windows opens the file for read and write while sharing only read
access. A second acquirer asks for write access as well, which the
holder’s share mode denies, and gets
ERROR_SHARING_VIOLATION. A reader asks only for read access, which the share mode permits — which is what lets the loser find out who beat it. - Unix takes
flock(LOCK_EX | LOCK_NB).flockis advisory and does not stand in the way of an ordinaryopenfor reading, so the loser can read the holder record there too. Locks are held per open file description, so a second acquisition from the same process is refused as firmly as one from another process.
§The lock file is never deleted
Not on release, not on a clean shutdown, not by Drop. On Unix a lock is a
property of the inode, so a holder that unlinks the file lets the next
acquirer create and lock a different inode — after which two processes
each hold “the lock” and neither can see the other. Leaving a zero-cost
empty file behind is the whole price of not having that bug.
§What “host-wide” means, precisely
The lock is a file under crate::paths::AppPaths::state_dir, as
05-infrastructure.md specifies (“state/ agent lock”). Two agents
contend if and only if they resolve the same state directory — which is what
makes the lock host-wide for every configuration this product supports, and
is worth stating rather than assuming: the platform-standard state directory
is per-account on all three operating systems, so a daemon running as a
service account and an interactive daemon run by a logged-in operator
resolve different paths and would not contend. That is why
05-infrastructure.md requires service install to record its resolved
configuration and why service status reports it. A future host-wide
machine lock (%ProgramData%, /var/lock) would need the installer to
create it with the right ownership, which is d3’s territory, not this
module’s.
Structs§
- Host
Lock - A held lock. Releasing it is dropping it.
- Lock
Holder - Who holds a lock, as recorded by the holder itself.