Graceful shutdown

This commit is contained in:
Igor Katson 2024-08-26 18:25:22 +01:00
parent 4ae22f2a3d
commit bf9d75e748
No known key found for this signature in database
GPG key ID: B4EC22B66D61A3F5
6 changed files with 81 additions and 27 deletions

View file

@ -46,6 +46,8 @@ bytes = "1.5.0"
openssl = { version = "0.10", features = ["vendored"], optional = true }
upnp-serve = { path = "../upnp-serve" }
libc = "0.2.158"
signal-hook = "0.3.17"
tokio-util = "0.7.11"
[dev-dependencies]
futures = { version = "0.3" }

View file

@ -1,4 +1,4 @@
use std::{io, net::SocketAddr, path::PathBuf, sync::Arc, time::Duration};
use std::{io, net::SocketAddr, path::PathBuf, sync::Arc, thread, time::Duration};
use anyhow::{bail, Context};
use clap::{CommandFactory, Parser, ValueEnum};
@ -17,7 +17,8 @@ use librqbit::{
};
use size_format::SizeFormatterBinary as SF;
use tokio::net::TcpListener;
use tracing::{error, error_span, info, trace_span, warn};
use tokio_util::sync::CancellationToken;
use tracing::{debug, error, error_span, info, trace_span, warn};
#[derive(Debug, Clone, Copy, ValueEnum)]
enum LogLevel {
@ -357,10 +358,30 @@ fn main() -> anyhow::Result<()> {
.max_blocking_threads(opts.max_blocking_threads as usize)
.build()?;
rt.block_on(async_main(opts))
let token = tokio_util::sync::CancellationToken::new();
{
let token = token.clone();
use signal_hook::{consts::SIGINT, consts::SIGTERM, iterator::Signals};
let mut signals = Signals::new([SIGINT, SIGTERM])?;
thread::spawn(move || {
if let Some(sig) = signals.forever().next() {
warn!("Received signal {:?}", sig);
token.cancel();
}
});
}
rt.block_on(async move {
let res = async_main(opts, token.clone()).await;
if let Err(e) = res {
error!("error running rqbit: {e:?}");
std::process::exit(1);
}
std::process::exit(0);
})
}
async fn async_main(opts: Opts) -> anyhow::Result<()> {
async fn async_main(opts: Opts, cancel: CancellationToken) -> anyhow::Result<()> {
let log_config = init_logging(InitLoggingOptions {
default_rust_log_value: Some(match opts.log_level.unwrap_or(LogLevel::Info) {
LogLevel::Trace => "trace",
@ -420,6 +441,7 @@ async fn async_main(opts: Opts) -> anyhow::Result<()> {
concurrent_init_limit: Some(opts.concurrent_init_limit),
root_span: None,
fastresume: false,
cancellation_token: Some(cancel.clone()),
};
let stats_printer = |session: Arc<Session>| async move {
@ -553,15 +575,15 @@ async fn async_main(opts: Opts) -> anyhow::Result<()> {
Some(srv) => {
let upnp_fut = srv.run_ssdp_forever();
tokio::pin!(http_api_fut);
tokio::pin!(upnp_fut);
tokio::select! {
r = &mut http_api_fut => r,
r = &mut upnp_fut => r
r = http_api_fut => r,
r = upnp_fut => r
}
}
None => http_api_fut.await,
None => tokio::select! {
_ = cancel.cancelled() => bail!("cancelled"),
r = http_api_fut => r,
},
};
res.context("error running rqbit server")
@ -726,8 +748,13 @@ async fn async_main(opts: Opts) -> anyhow::Result<()> {
if download_opts.exit_on_finish {
let results = futures::future::join_all(
handles.iter().map(|h| h.wait_until_completed()),
)
.await;
);
let results = tokio::select! {
_ = cancel.cancelled() => {
bail!("cancelled");
},
r = results => r
};
if results.iter().any(|r| r.is_err()) {
anyhow::bail!("some downloads failed")
}
@ -735,9 +762,8 @@ async fn async_main(opts: Opts) -> anyhow::Result<()> {
Ok(())
} else {
// Sleep forever.
loop {
tokio::time::sleep(Duration::from_secs(60)).await;
}
cancel.cancelled().await;
bail!("cancelled");
}
} else {
anyhow::bail!("no torrents were added")