| Safe Haskell | None |
|---|---|
| Language | GHC2021 |
Ecluse.Core.Cve.Slot
Description
The read side of the advisory database's atomic shadow-swap: a slot holding
the currently-active CveDb generation, read through a bracket so the swap can
tell when a superseded generation is no longer required for reads.
One slot serves one ecosystem's artifact. Rule evaluations borrow the current
generation's CveLookup view through withSlotLookup (the composition root
installs it as rdWithCveLookup); the sync task installs a
newly-verified generation with swapIn, which waits for the displaced
generation's readers to drain and then closes it. Closing is also the
reclamation: the sync task has already renamed the new artifact over the old
one's only file name, so the drained close releases the old inode's last
reference and the kernel frees the storage. Pruning is a property the OS
enforces, never a delete this code could mistime.
Before the first successful sync the slot is empty and hands readers
Nothing; the CVE rule abstains and the ordinary policy governs.
Documentation
withSlotLookup :: CveSlot -> (Maybe CveLookup -> IO a) -> IO a Source #
Borrow the current generation's view for the duration of one action. The
generation is pinned (its reader count held above zero) for exactly the
bracket, so a concurrent swapIn cannot close it mid-read; a swap landing
during the action only means the next bracket sees the new generation.
swapIn :: CveSlot -> CveDb -> IO () Source #
Install a newly-verified generation and retire the one it displaces:
publish the new CveDb to readers atomically, wait for the displaced
generation's readers to drain to zero, then close it, releasing the old
artifact's last inode reference (see the module header). Blocks only the
caller (the sync task), and only for as long as the longest in-flight
evaluation, which the rule's resilience timeout already bounds.
The slot owns the new database from the moment this is entered: publication is the first effect and is atomic, so no failure mode of this call leaves the new generation both unpublished and unclosed, and no caller cleanup may close it. A close failure on the displaced generation is swallowed (the swap already succeeded; the stale connection is the only casualty), while cancellation during the drain wait propagates, leaving the new generation live and the displaced one unclosed until process exit.
Safe under a single swapper (the one sync task per slot); with several, each call retires exactly the generation it displaced.