use std::time::Duration;
use either::Either::{Left, Right};
use rand::RngCore;
use crate::cluster::{Cluster, ClusterError};
use crate::lock;
pub fn run_and_stop<'a, F, T>(
cluster: &'a Cluster,
lock: lock::UnlockedFile,
action: F,
) -> Result<T, ClusterError>
where
F: std::panic::UnwindSafe + FnOnce(&'a Cluster) -> T,
{
let lock = startup(cluster, lock)?;
let action_res = std::panic::catch_unwind(|| action(cluster));
let _: Option<bool> = shutdown(cluster, lock, Cluster::stop)?;
match action_res {
Ok(result) => Ok(result),
Err(err) => std::panic::resume_unwind(err),
}
}
pub fn run_and_destroy<'a, F, T>(
cluster: &'a Cluster,
lock: lock::UnlockedFile,
action: F,
) -> Result<T, ClusterError>
where
F: std::panic::UnwindSafe + FnOnce(&'a Cluster) -> T,
{
let lock = startup(cluster, lock)?;
let action_res = std::panic::catch_unwind(|| action(cluster));
let shutdown_res = shutdown(cluster, lock, Cluster::destroy);
match action_res {
Ok(result) => shutdown_res.map(|_| result),
Err(err) => std::panic::resume_unwind(err),
}
}
fn startup(
cluster: &Cluster,
mut lock: lock::UnlockedFile,
) -> Result<lock::LockedFileShared, ClusterError> {
loop {
lock = match lock.try_lock_exclusive() {
Ok(Left(lock)) => {
let lock = lock.lock_shared()?;
if cluster.running()? {
return Ok(lock);
}
let lock = lock.unlock()?;
let delay = rand::thread_rng().next_u32();
let delay = 200 + (delay % 800);
let delay = Duration::from_millis(u64::from(delay));
std::thread::sleep(delay);
lock
}
Ok(Right(lock)) => {
cluster.start()?;
return Ok(lock.lock_shared()?);
}
Err(err) => return Err(err.into()),
};
}
}
fn shutdown<F, T>(
cluster: &Cluster,
lock: lock::LockedFileShared,
action: F,
) -> Result<Option<T>, ClusterError>
where
F: FnOnce(&Cluster) -> Result<T, ClusterError>,
{
match lock.try_lock_exclusive() {
Ok(Left(lock)) => {
lock.unlock()?;
Ok(None)
}
Ok(Right(lock)) => match action(cluster) {
Ok(result) => {
lock.unlock()?;
Ok(Some(result))
}
Err(err) => Err(err),
},
Err(err) => Err(err.into()),
}
}