diff --git a/src/subsystem/subsystem_handle.rs b/src/subsystem/subsystem_handle.rs index 702dacc..0cc4c99 100644 --- a/src/subsystem/subsystem_handle.rs +++ b/src/subsystem/subsystem_handle.rs @@ -165,7 +165,7 @@ impl SubsystemHandle { } /// Waits until all the children of this subsystem are finished. - pub async fn wait_for_children(&mut self) { + pub async fn wait_for_children(&self) { self.inner.joiner_token.join_children().await } diff --git a/src/utils/joiner_token.rs b/src/utils/joiner_token.rs index a60ecc5..4973791 100644 --- a/src/utils/joiner_token.rs +++ b/src/utils/joiner_token.rs @@ -70,9 +70,7 @@ impl JoinerToken { (Self { inner }, weak_ref) } - // Requires `mut` access to prevent children from being spawned - // while waiting - pub(crate) async fn join_children(&mut self) { + pub(crate) async fn join_children(&self) { let mut subscriber = self.inner.counter.subscribe(); // Ignore errors; if the channel got closed, that definitely means diff --git a/tests/integration_test_2.rs b/tests/integration_test_2.rs index 9748f58..6510fe3 100644 --- a/tests/integration_test_2.rs +++ b/tests/integration_test_2.rs @@ -70,7 +70,7 @@ async fn wait_for_children() { BoxedResult::Ok(()) }; - let subsys1 = move |mut subsys: SubsystemHandle| async move { + let subsys1 = move |subsys: SubsystemHandle| async move { subsys.start(SubsystemBuilder::new("nested1", nested_subsys1)); sleep(Duration::from_millis(100)).await; @@ -97,3 +97,64 @@ async fn wait_for_children() { .await .unwrap(); } + +#[tokio::test] +#[traced_test] +async fn request_local_shutdown() { + let (nested1_started, set_nested1_started) = Event::create(); + let (nested1_finished, set_nested1_finished) = Event::create(); + let (nested2_started, set_nested2_started) = Event::create(); + let (nested2_finished, set_nested2_finished) = Event::create(); + let (global_finished, set_global_finished) = Event::create(); + + let nested_subsys2 = move |subsys: SubsystemHandle| async move { + set_nested2_started(); + subsys.on_shutdown_requested().await; + set_nested2_finished(); + BoxedResult::Ok(()) + }; + + let nested_subsys1 = move |subsys: SubsystemHandle| async move { + subsys.start(SubsystemBuilder::new("nested2", nested_subsys2)); + set_nested1_started(); + subsys.on_shutdown_requested().await; + set_nested1_finished(); + BoxedResult::Ok(()) + }; + + let subsys1 = move |subsys: SubsystemHandle| async move { + subsys.start(SubsystemBuilder::new("nested1", nested_subsys1)); + + sleep(Duration::from_millis(100)).await; + + assert!(nested1_started.get()); + assert!(!nested1_finished.get()); + assert!(nested2_started.get()); + assert!(!nested2_finished.get()); + assert!(!global_finished.get()); + + subsys.request_local_shutdown(); + sleep(Duration::from_millis(200)).await; + + assert!(nested1_finished.get()); + assert!(nested2_finished.get()); + assert!(!global_finished.get()); + + subsys.request_shutdown(); + sleep(Duration::from_millis(50)).await; + + assert!(global_finished.get()); + + BoxedResult::Ok(()) + }; + + Toplevel::new(move |s| async move { + s.start(SubsystemBuilder::new("subsys", subsys1)); + + s.on_shutdown_requested().await; + set_global_finished(); + }) + .handle_shutdown_requests(Duration::from_millis(100)) + .await + .unwrap(); +}