cargo-binstall/crates/bin/src/bin_util.rs
Jiahao XU c5d0b84aa6
Perform startup/init code eagerly in entry::install_crates (#880)
The startup/init code in `entry::install_crates` performs a lot of blocking operations, from testing if dir exists to reading from files and there is no `.await` in there until after the startup.

There are also a few cases where `block_in_place` should be called (e.g. loading manifests, loading TLS certificates) but is missing.

Most of the `Args` passed to `entry::install_crates` are actually consumed before the first `.await` point, so performing startup/init code eagerly would make the generated future much smaller, reduce codegen and also makes it easier to optimize.

Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com>
2023-03-11 15:06:46 +11:00

67 lines
1.8 KiB
Rust

use std::{
future::Future,
process::{ExitCode, Termination},
time::Duration,
};
use binstalk::errors::BinstallError;
use binstalk::helpers::{signal::cancel_on_user_sig_term, tasks::AutoAbortJoinHandle};
use miette::Result;
use tokio::runtime::Runtime;
use tracing::{error, info};
pub enum MainExit {
Success(Option<Duration>),
Error(BinstallError),
Report(miette::Report),
}
impl Termination for MainExit {
fn report(self) -> ExitCode {
match self {
Self::Success(spent) => {
if let Some(spent) = spent {
info!("Done in {spent:?}");
}
ExitCode::SUCCESS
}
Self::Error(err) => err.report(),
Self::Report(err) => {
error!("Fatal error:\n{err:?}");
ExitCode::from(16)
}
}
}
}
impl MainExit {
pub fn new(res: Result<()>, done: Duration) -> Self {
res.map(|()| MainExit::Success(Some(done)))
.unwrap_or_else(|err| {
err.downcast::<BinstallError>()
.map(MainExit::Error)
.unwrap_or_else(MainExit::Report)
})
}
}
/// This function would start a tokio multithreading runtime,
/// spawn a new task on it that runs `f()`, then `block_on` it.
///
/// It will cancel the future if user requested cancellation
/// via signal.
pub fn run_tokio_main<Func, Fut>(f: Func) -> Result<()>
where
Func: FnOnce() -> Result<Option<Fut>>,
Fut: Future<Output = Result<()>> + Send + 'static,
{
let rt = Runtime::new().map_err(BinstallError::from)?;
let _guard = rt.enter();
if let Some(fut) = f()? {
let handle = AutoAbortJoinHandle::new(rt.spawn(fut));
rt.block_on(cancel_on_user_sig_term(handle))?
} else {
Ok(())
}
}