Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

- **R-15 Gate 4E:** the recovery of a prepared root that changes the live-migration binding now needs the exclusive admission. `recover_root` refuses to complete such a root (typed, before any sync or write; the preparation stays) and the ordinary operation that ran it is refused with `RecoveryPending`; the new `recover_root_exclusive` completes it, and `begin_conversion` resolves a pending preparation through it before it reads the journal (it now takes the provider by mutable reference). A prepared root that keeps the binding still completes under a shared admission. The classification compares the authenticated committed and prepared roots; no journal locator, no E3 writer and no `PREPARE`/`DONE` window. Issue #359. PR #1035.

- **R-15 Gate 4E:** a journal phase change is now published only by `commit_phase_transition`, which takes the installation's exclusive admission by type. The progress checkpoint, the binding advance and the journal-level checkpoint publish refuse a successor that changes the phase, and the transition refuses one that keeps it, each before a journal byte is written. The conversion driver's own phase changes use the new commit and behave as before. The raw root commit is no longer exported by the production crate, only with the test-only `test-support` feature, so it cannot be used to name a phase change. The capture, takeover and renewal writers, the writer journal locator and the PREPARE/DONE windows are unchanged and stay closed. Issue #359. PR #1033, which was closed unmerged: its six commits landed through PR #1035.

- **Operations:** add a versioned Vercel Preview retention runner for the post-merge
reconciliation. `pnpm run vercel:retention:dry-run` is read-only and
`pnpm run vercel:retention:reconcile` runs the full pass, both without arguments. A pass
Expand Down
38 changes: 34 additions & 4 deletions crates/worldscript-secure-storage/src/admission.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,9 +169,37 @@ impl<Mode> AdmissionGuard<Mode> {
}

/// One finite root event below the SAME admission. Busy never upgrades or releases admission.
///
/// The event carries the mode of the admission it was taken from, so an operation that needs the
/// exclusive admission can ask for it in its signature: an event of a shared admission is another
/// type and does not compile where an exclusive one is required.
///
/// ```compile_fail
/// use worldscript_secure_storage::admission::{
/// AdmittedRootCommitGuard, ExclusiveAdmission, SharedAdmission,
/// };
///
/// fn needs_exclusive(_: &AdmittedRootCommitGuard<'_, ExclusiveAdmission>) {}
///
/// fn shared_event_is_refused(event: &AdmittedRootCommitGuard<'_, SharedAdmission>) {
/// needs_exclusive(event);
/// }
/// ```
///
/// and the same call with an exclusive event compiles:
///
/// ```
/// use worldscript_secure_storage::admission::{AdmittedRootCommitGuard, ExclusiveAdmission};
///
/// fn needs_exclusive(_: &AdmittedRootCommitGuard<'_, ExclusiveAdmission>) {}
///
/// fn exclusive_event_is_accepted(event: &AdmittedRootCommitGuard<'_, ExclusiveAdmission>) {
/// needs_exclusive(event);
/// }
/// ```
pub fn try_root_commit(
&mut self,
) -> Result<Option<AdmittedRootCommitGuard<'_>>, AdmissionError> {
) -> Result<Option<AdmittedRootCommitGuard<'_, Mode>>, AdmissionError> {
if !self.held.is_current() {
return Err(AdmissionError::IdentityChanged);
}
Expand All @@ -184,6 +212,7 @@ impl<Mode> AdmissionGuard<Mode> {
Ok(Some(AdmittedRootCommitGuard {
root,
admission: &mut self.held,
mode: PhantomData,
}))
}
}
Expand Down Expand Up @@ -267,14 +296,15 @@ impl HeldAdmission {
}
}

/// A root mutex event that cannot outlive its operation admission.
/// A root mutex event that cannot outlive its operation admission, of the mode of that admission.
#[derive(Debug)]
pub struct AdmittedRootCommitGuard<'a> {
pub struct AdmittedRootCommitGuard<'a, Mode> {
root: RootCommitGuard,
admission: &'a mut HeldAdmission,
mode: PhantomData<Mode>,
}

impl AdmittedRootCommitGuard<'_> {
impl<Mode> AdmittedRootCommitGuard<'_, Mode> {
/// The 4A guard, only while both identities still match. Root APIs also revalidate their guard.
pub fn root_guard(&self) -> Result<&RootCommitGuard, AdmissionError> {
if self.admission.is_current() && self.root.guards(&self.admission.root_dir) {
Expand Down
Loading
Loading