bacnet_network/router/
shutdown.rs1use std::fmt;
4use std::future::{Future, poll_fn};
5use std::panic::{AssertUnwindSafe, catch_unwind};
6use std::sync::Arc;
7use std::task::Poll;
8
9use bacnet_transport::port::TransportPort;
10use bacnet_types::error::Error;
11use tokio::sync::watch;
12use tokio::task::JoinHandle;
13
14#[derive(Debug)]
16pub enum RouterStopFailure {
17 Transport {
19 network_number: u16,
21 error: Error,
23 },
24 Worker {
26 network_number: Option<u16>,
28 worker: &'static str,
30 detail: String,
32 },
33}
34
35#[derive(Clone, Debug)]
41pub struct RouterStopError(Arc<[RouterStopFailure]>);
42
43impl RouterStopError {
44 pub fn failures(&self) -> &[RouterStopFailure] {
46 &self.0
47 }
48
49 fn from_failures(failures: Vec<RouterStopFailure>) -> Result<(), Self> {
50 if failures.is_empty() {
51 Ok(())
52 } else {
53 Err(Self(failures.into()))
54 }
55 }
56}
57
58impl fmt::Display for RouterStopError {
59 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60 write!(f, "router shutdown failed: {:?}", self.0)
61 }
62}
63
64impl std::error::Error for RouterStopError {
65 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
66 self.0.iter().find_map(|failure| match failure {
67 RouterStopFailure::Transport { error, .. } => {
68 Some(error as &(dyn std::error::Error + 'static))
69 }
70 RouterStopFailure::Worker { .. } => None,
71 })
72 }
73}
74
75pub(super) async fn observe<F: Future>(future: F) -> Result<F::Output, String> {
78 let mut future = std::pin::pin!(future);
79 poll_fn(
80 |cx| match catch_unwind(AssertUnwindSafe(|| future.as_mut().poll(cx))) {
81 Ok(Poll::Pending) => Poll::Pending,
82 Ok(Poll::Ready(value)) => Poll::Ready(Ok(value)),
83 Err(panic) => {
84 let detail = panic
85 .downcast_ref::<String>()
86 .cloned()
87 .or_else(|| panic.downcast_ref::<&str>().map(|s| (*s).to_owned()))
88 .unwrap_or_else(|| "non-string panic payload".into());
89 Poll::Ready(Err(detail))
90 }
91 },
92 )
93 .await
94}
95
96pub(super) struct TransportOwner<T: TransportPort> {
99 pub transport: T,
100 pub stopped: bool,
101}
102
103impl<T: TransportPort> Drop for TransportOwner<T> {
104 fn drop(&mut self) {
105 if !self.stopped {
106 self.transport.abort();
107 }
108 }
109}
110
111pub(super) struct Workers {
112 pub dispatch: Vec<JoinHandle<()>>,
113 pub senders: Vec<(u16, JoinHandle<Vec<RouterStopFailure>>)>,
114 pub aging: JoinHandle<()>,
115 pub limits: Vec<JoinHandle<()>>,
116 pub stop_senders: watch::Sender<bool>,
117}
118
119impl Workers {
120 async fn stop(mut self) -> Result<(), RouterStopError> {
121 self.stop_senders.send_replace(true);
124 for task in &self.dispatch {
125 task.abort();
126 }
127 self.aging.abort();
128 for task in &self.limits {
129 task.abort();
130 }
131 let mut failures = Vec::new();
132 for task in &mut self.dispatch {
133 joined(task, "dispatch", &mut failures).await;
134 }
135 joined(&mut self.aging, "aging", &mut failures).await;
136 for task in &mut self.limits {
137 joined(task, "limit", &mut failures).await;
138 }
139 for (network_number, task) in &mut self.senders {
140 match task.await {
141 Ok(port_failures) => failures.extend(port_failures),
142 Err(error) => failures.push(RouterStopFailure::Worker {
143 network_number: Some(*network_number),
144 worker: "sender owner",
145 detail: error.to_string(),
146 }),
147 }
148 }
149 RouterStopError::from_failures(failures)
150 }
151}
152
153impl Drop for Workers {
154 fn drop(&mut self) {
155 self.stop_senders.send_replace(true);
156 for task in &self.dispatch {
157 task.abort();
158 }
159 self.aging.abort();
160 for task in &self.limits {
161 task.abort();
162 }
163 }
166}
167
168async fn joined(
169 task: &mut JoinHandle<()>,
170 worker: &'static str,
171 failures: &mut Vec<RouterStopFailure>,
172) {
173 if let Err(error) = task.await
174 && !error.is_cancelled()
175 {
176 failures.push(RouterStopFailure::Worker {
177 network_number: None,
178 worker,
179 detail: error.to_string(),
180 });
181 }
182}
183
184pub(super) struct Lifecycle {
185 pub workers: Option<Workers>,
186 cleanup: Option<JoinHandle<Result<(), RouterStopError>>>,
187 settled: Option<Result<(), RouterStopError>>,
188}
189
190impl Lifecycle {
191 pub fn new(workers: Workers) -> Self {
192 Self {
193 workers: Some(workers),
194 cleanup: None,
195 settled: None,
196 }
197 }
198
199 pub fn begin(&mut self) {
200 if let Some(workers) = self.workers.take() {
201 if let Ok(runtime) = tokio::runtime::Handle::try_current() {
202 self.cleanup = Some(runtime.spawn(workers.stop()));
203 } else {
204 drop(workers);
206 }
207 }
208 }
209
210 pub async fn stop(&mut self) -> Result<(), RouterStopError> {
211 self.begin();
212 if self.settled.is_none() {
213 let result = match self.cleanup.as_mut() {
214 Some(task) => match task.await {
215 Ok(result) => result,
216 Err(error) => RouterStopError::from_failures(vec![RouterStopFailure::Worker {
217 network_number: None,
218 worker: "shutdown owner",
219 detail: error.to_string(),
220 }]),
221 },
222 None => RouterStopError::from_failures(vec![RouterStopFailure::Worker {
223 network_number: None,
224 worker: "shutdown owner",
225 detail: "cleanup owner unavailable".into(),
226 }]),
227 };
228 self.cleanup.take();
229 self.settled = Some(result);
230 }
231 self.settled.as_ref().unwrap().clone()
232 }
233}
234
235impl Drop for Lifecycle {
236 fn drop(&mut self) {
237 self.begin();
238 }
239}