diff --git a/zebrad/src/application.rs b/zebrad/src/application.rs index c6d5fbf6..576cdc41 100644 --- a/zebrad/src/application.rs +++ b/zebrad/src/application.rs @@ -3,7 +3,7 @@ use crate::{commands::ZebradCmd, config::ZebradConfig}; use abscissa_core::{ application::{self, AppCell}, - config, trace, Application, Component, EntryPoint, FrameworkError, StandardPaths, + config, trace, Application, EntryPoint, FrameworkError, StandardPaths, }; /// Application state diff --git a/zebrad/src/commands/connect.rs b/zebrad/src/commands/connect.rs index c6b713d2..038f6011 100644 --- a/zebrad/src/commands/connect.rs +++ b/zebrad/src/commands/connect.rs @@ -26,13 +26,18 @@ impl Runnable for ConnectCmd { info!(connect.addr = ?self.addr); use crate::components::tokio::TokioComponent; - let _ = app_writer() + let rt = app_writer() .state_mut() .components .get_downcast_mut::() .expect("TokioComponent should be available") .rt - .block_on(self.connect()); + .take(); + + rt.expect("runtime should not already be taken") + .block_on(self.connect()) + // Surface any error that occurred executing the future. + .unwrap(); } } @@ -44,11 +49,9 @@ impl ConnectCmd { use tower::{buffer::Buffer, service_fn, Service, ServiceExt}; let node = Buffer::new( - service_fn(|req| { - async move { - info!(?req); - Ok::(Response::Ok) - } + service_fn(|req| async move { + info!(?req); + Ok::(Response::Ok) }), 1, ); diff --git a/zebrad/src/commands/seed.rs b/zebrad/src/commands/seed.rs index 3520c731..1c12cbeb 100644 --- a/zebrad/src/commands/seed.rs +++ b/zebrad/src/commands/seed.rs @@ -112,13 +112,18 @@ impl Runnable for SeedCmd { fn run(&self) { use crate::components::tokio::TokioComponent; - let _ = app_writer() + let rt = app_writer() .state_mut() .components .get_downcast_mut::() .expect("TokioComponent should be available") .rt - .block_on(self.seed()); + .take(); + + rt.expect("runtime should not already be taken") + .block_on(self.seed()) + // Surface any error that occurred executing the future. + .unwrap(); } } diff --git a/zebrad/src/commands/start.rs b/zebrad/src/commands/start.rs index ec8a39c9..4f6ebd26 100644 --- a/zebrad/src/commands/start.rs +++ b/zebrad/src/commands/start.rs @@ -40,12 +40,15 @@ impl Runnable for StartCmd { use crate::components::tokio::TokioComponent; - app_writer() + let rt = app_writer() .state_mut() .components .get_downcast_mut::() .expect("TokioComponent should be available") .rt + .take(); + + rt.expect("runtime should not already be taken") .block_on(future::pending::<()>()); } } diff --git a/zebrad/src/components/tokio.rs b/zebrad/src/components/tokio.rs index 0487585e..059368ad 100644 --- a/zebrad/src/components/tokio.rs +++ b/zebrad/src/components/tokio.rs @@ -5,15 +5,21 @@ use abscissa_core::{Component, FrameworkError}; use tokio::runtime::Runtime; /// An Abscissa component which owns a Tokio runtime. +/// +/// The runtime is stored as an `Option` so that when it's time to enter an async +/// context by calling `block_on` with a "root future", the runtime can be taken +/// independently of Abscissa's component locking system. Otherwise whatever +/// calls `block_on` holds an application lock for the entire lifetime of the +/// async context. #[derive(Component, Debug)] pub struct TokioComponent { - pub rt: Runtime, + pub rt: Option, } impl TokioComponent { pub fn new() -> Result { Ok(Self { - rt: Runtime::new().unwrap(), + rt: Some(Runtime::new().unwrap()), }) } } diff --git a/zebrad/src/components/tracing.rs b/zebrad/src/components/tracing.rs index 228d5472..260db884 100644 --- a/zebrad/src/components/tracing.rs +++ b/zebrad/src/components/tracing.rs @@ -47,23 +47,27 @@ impl TracingEndpoint { .parse() .expect("Hardcoded address should be parseable"); - tokio_component.rt.spawn(async move { - // try_bind uses the tokio runtime, so we - // need to construct it inside the task. - let server = match Server::try_bind(&addr) { - Ok(s) => s, - Err(e) => { - error!("Could not open tracing endpoint listener"); - error!("Error: {}", e); - return; + tokio_component + .rt + .as_ref() + .expect("runtime should not be taken") + .spawn(async move { + // try_bind uses the tokio runtime, so we + // need to construct it inside the task. + let server = match Server::try_bind(&addr) { + Ok(s) => s, + Err(e) => { + error!("Could not open tracing endpoint listener"); + error!("Error: {}", e); + return; + } } - } - .serve(service); + .serve(service); - if let Err(e) = server.await { - error!("Server error: {}", e); - } - }); + if let Err(e) = server.await { + error!("Server error: {}", e); + } + }); Ok(()) } diff --git a/zebrad/src/prelude.rs b/zebrad/src/prelude.rs index 78a21bb3..b4d6eb83 100644 --- a/zebrad/src/prelude.rs +++ b/zebrad/src/prelude.rs @@ -6,10 +6,3 @@ pub use crate::application::{app_config, app_reader, app_writer}; /// Commonly used Abscissa traits pub use abscissa_core::{Application, Command, Runnable}; - -/// Type alias to make working with tower traits easier. -/// -/// Note: the 'static lifetime bound means that the *type* cannot have any -/// non-'static lifetimes, (e.g., when a type contains a borrow and is -/// parameterized by 'a), *not* that the object itself has 'static lifetime. -pub(crate) use abscissa_core::error::BoxError;