use std::sync::{Arc, Mutex, MutexGuard};
use saddle_admission::VerifiedPostDriverInstallBinding;
use saddle_core::{ErrorKind, Result, SaddleError};
use crate::compiled_route::OfficialCompiledDriverFinalizer;
enum InstallState {
Unarmed,
MustSubmit,
Consumed,
}
struct State {
install: InstallState,
submitted: Option<OfficialCompiledDriverFinalizer>,
consumed: bool,
}
#[derive(Clone)]
pub struct PendingDriverFinalizerSlot {
state: Arc<Mutex<State>>,
}
impl PendingDriverFinalizerSlot {
pub(crate) fn new() -> Self {
Self {
state: Arc::new(Mutex::new(State {
install: InstallState::Unarmed,
submitted: None,
consumed: false,
})),
}
}
#[cfg(test)]
pub(crate) fn is_unarmed_for_test(&self) -> bool {
let state = lock(&self.state);
matches!(state.install, InstallState::Unarmed)
&& state.submitted.is_none()
&& !state.consumed
}
#[doc(hidden)]
pub fn commit_verified_install(&self, binding: VerifiedPostDriverInstallBinding) {
let mut state = lock(&self.state);
if !matches!(state.install, InstallState::Unarmed)
|| state.submitted.is_some()
|| state.consumed
{
std::process::abort();
}
let _binding = binding;
state.install = InstallState::MustSubmit;
}
pub(crate) fn reserved_submit_handle(&self) -> MustSubmitDriverFinalizer {
MustSubmitDriverFinalizer { slot: self.clone() }
}
pub fn submit(&self, finalizer: OfficialCompiledDriverFinalizer) {
let mut state = lock(&self.state);
if !matches!(state.install, InstallState::MustSubmit)
|| state.submitted.is_some()
|| state.consumed
{
std::process::abort();
}
state.submitted = Some(finalizer);
}
pub(crate) fn finish(
&self,
runtime: tokio::runtime::Runtime,
application_result: Result<()>,
) -> Result<()> {
let pending = {
let mut state = lock(&self.state);
if state.consumed {
std::process::abort();
}
state.consumed = true;
let must_submit = matches!(state.install, InstallState::MustSubmit);
state.install = InstallState::Consumed;
if must_submit {
Some(
state
.submitted
.take()
.unwrap_or_else(|| std::process::abort()),
)
} else {
None
}
};
let finalizer_result = match pending {
Some(pending) => pending
.bind_runtime(runtime)
.finish()
.map_err(|_| finalization_error())
.and_then(|report| {
if report.ledger.healthy && !report.watermark.breached && !report.task_failed {
Ok(())
} else {
Err(finalization_error())
}
}),
None => {
drop(runtime);
Ok(())
}
};
application_result.and(finalizer_result)
}
}
#[doc(hidden)]
pub struct MustSubmitDriverFinalizer {
slot: PendingDriverFinalizerSlot,
}
impl MustSubmitDriverFinalizer {
#[doc(hidden)]
pub fn submit(self, finalizer: OfficialCompiledDriverFinalizer) {
self.slot.submit(finalizer);
}
}
fn lock(state: &Mutex<State>) -> MutexGuard<'_, State> {
state.lock().unwrap_or_else(|_| std::process::abort())
}
fn finalization_error() -> SaddleError {
SaddleError::new(
ErrorKind::Infrastructure,
"runtime.driver_finalization_failed",
"the managed Runtime driver did not finalize cleanly",
)
}