use std::sync::{Arc, Mutex, OnceLock};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum IdentityRecoveryFailStopPoint {
ManifestCommittedBeforeReply,
ManifestReplySent,
LeaseAcquired,
RuntimeCasApplied,
MemberEventApplied,
}
type IdentityRecoveryFailStopHook = Arc<dyn Fn() + Send + Sync + 'static>;
static IDENTITY_RECOVERY_FAIL_STOP_HOOK: OnceLock<
Mutex<Option<(IdentityRecoveryFailStopPoint, IdentityRecoveryFailStopHook)>>,
> = OnceLock::new();
#[doc(hidden)]
pub fn arm_identity_recovery_fail_stop_for_test(
point: IdentityRecoveryFailStopPoint,
hook: IdentityRecoveryFailStopHook,
) -> Result<(), &'static str> {
let mut armed = IDENTITY_RECOVERY_FAIL_STOP_HOOK
.get_or_init(|| Mutex::new(None))
.lock()
.map_err(|_| "identity recovery fail-stop hook mutex is poisoned")?;
if armed.is_some() {
return Err("identity recovery fail-stop hook is already armed");
}
*armed = Some((point, hook));
Ok(())
}
pub(crate) fn trigger_identity_recovery_fail_stop(point: IdentityRecoveryFailStopPoint) {
let hook = {
let Ok(mut armed) = IDENTITY_RECOVERY_FAIL_STOP_HOOK
.get_or_init(|| Mutex::new(None))
.lock()
else {
return;
};
match armed.as_ref() {
Some((armed_point, _)) if *armed_point == point => armed.take().map(|(_, hook)| hook),
_ => None,
}
};
if let Some(hook) = hook {
hook();
}
}