driver_manager_shutdown/
node_shutdown_coordinator.rs1use crate::node_removal_tracker::{NodeId, NodeInfo, NodeRemovalTracker};
6use crate::shutdown_node::ShutdownNode;
7use async_trait::async_trait;
8use driver_manager_driver_host::DriverHost;
9use driver_manager_types::{Collection, ShutdownState};
10use fuchsia_async as fasync;
11use log::debug;
12use rand::Rng;
13use std::cell::RefCell;
14use std::rc::{Rc, Weak};
15
16const MIN_TEST_DELAY_MS: u32 = 0;
18const MAX_TEST_DELAY_MS: u32 = 5;
19
20#[derive(Clone, Copy)]
21pub enum RemovalSet {
22 All,
23 Package,
24}
25
26#[derive(Copy, Clone, PartialEq, Debug)]
27pub enum ShutdownIntent {
28 Removal,
29 Restart,
30 RebindComposite,
31 Quarantine,
32}
33
34#[async_trait(?Send)]
35pub trait NodeShutdownBridge {
36 fn get_removal_tracker_info(&self, state: ShutdownState) -> NodeInfo;
37 fn stop_driver(&self);
38 fn stop_driver_component(&self);
39 fn is_pending_bind(&self) -> bool;
40 fn has_children(&self) -> bool;
41 fn has_driver(&self) -> bool;
42 fn has_driver_component(&self) -> bool;
43 fn collection(&self) -> Collection;
44 fn children(&self) -> Vec<Rc<dyn ShutdownNode>>;
45 fn get_weak_node(&self) -> Weak<dyn ShutdownNode>;
46 fn has_driver_component_controller(&self) -> bool;
47 fn maybe_destroy_driver_component(&self, intent: ShutdownIntent) -> bool;
48 fn get_driver_host(&self) -> Option<Rc<dyn DriverHost>>;
49}
50
51pub struct NodeShutdownCoordinator {
52 pub(crate) bridge: Box<dyn NodeShutdownBridge>,
53 enable_test_shutdown_delays: bool,
54 rng_gen: Weak<RefCell<rand::rngs::StdRng>>,
55 removal_id: Option<NodeId>,
56 pub(crate) removal_tracker: Option<Weak<RefCell<NodeRemovalTracker>>>,
57 pub(crate) node_state: ShutdownState,
58 pub shutdown_intent: ShutdownIntent,
59 pub(crate) pending_task: Option<fasync::Task<()>>,
60}
61
62impl NodeShutdownCoordinator {
63 pub fn new(
64 bridge: Box<dyn NodeShutdownBridge>,
65 enable_test_shutdown_delays: bool,
66 rng_gen: Weak<RefCell<rand::rngs::StdRng>>,
67 ) -> Self {
68 Self {
69 bridge,
70 enable_test_shutdown_delays,
71 rng_gen,
72 removal_id: None,
73 removal_tracker: None,
74 node_state: ShutdownState::Running,
75 shutdown_intent: ShutdownIntent::Removal,
76 pending_task: None,
77 }
78 }
79
80 pub fn remove(
81 node: Rc<dyn ShutdownNode>,
82 mut removal_set: RemovalSet,
83 removal_tracker: Option<Weak<RefCell<NodeRemovalTracker>>>,
84 ) {
85 let mut nodes_to_check: Vec<Rc<dyn ShutdownNode>> = vec![];
86 let mut stack = vec![(node, removal_set)];
87
88 while let Some((current_node, current_set)) = stack.pop() {
89 let mut coordinator = current_node.get_shutdown_coordinator();
90 if let Some(tracker) = &removal_tracker {
91 coordinator.set_removal_tracker(tracker.clone());
92 }
93
94 debug!(
95 "Remove called on Node: {} state {:?}",
96 current_node.name(),
97 coordinator.node_state
98 );
99
100 match (coordinator.node_state, current_set) {
101 (ShutdownState::Prestop, RemovalSet::Package) => continue,
102 (ShutdownState::Running | ShutdownState::Stopped, RemovalSet::Package)
103 if matches!(
104 coordinator.bridge.collection(),
105 Collection::Boot | Collection::None
106 ) =>
107 {
108 coordinator.node_state = ShutdownState::Prestop;
109 }
110 (ShutdownState::Running | ShutdownState::Prestop | ShutdownState::Stopped, _) => {
111 coordinator.node_state = ShutdownState::WaitingOnDriverBind;
112 removal_set = RemovalSet::All;
113 }
114 _ => continue,
115 }
116
117 coordinator.notify_removal_tracker();
118
119 for child in coordinator.bridge.children() {
120 child.set_should_destroy_driver_component(true);
121 stack.push((child, removal_set));
122 }
123 nodes_to_check.push(current_node.clone());
124 }
125
126 for node in nodes_to_check {
127 node.get_shutdown_coordinator().check_node_state();
128 }
129 }
130
131 pub fn check_node_state(&mut self) {
132 if self.pending_task.is_some() {
133 return;
134 }
135 match self.node_state {
136 ShutdownState::Running | ShutdownState::Prestop | ShutdownState::Destroyed => {}
137 ShutdownState::Stopped => self.check_stopped(),
138 ShutdownState::WaitingOnDestroy => self.check_waiting_on_destroy(),
139 ShutdownState::WaitingOnDriverBind => self.check_waiting_on_driver_bind(),
140 ShutdownState::WaitingOnChildren => self.check_waiting_on_children(),
141 ShutdownState::WaitingOnDriver => self.check_waiting_on_driver(),
142 ShutdownState::WaitingOnDriverComponent => self.check_waiting_on_driver_component(),
143 }
144 }
145
146 fn check_waiting_on_driver_bind(&mut self) {
147 if self.bridge.is_pending_bind() {
148 return;
149 }
150 self.perform_transition(ShutdownState::WaitingOnChildren);
151 }
152
153 fn check_waiting_on_children(&mut self) {
154 if self.bridge.has_children() {
155 return;
156 }
157 self.perform_transition(ShutdownState::WaitingOnDriver);
158 }
159
160 fn check_waiting_on_driver(&mut self) {
161 if self.bridge.has_driver() {
162 return;
163 }
164 self.perform_transition(ShutdownState::WaitingOnDriverComponent);
165 }
166
167 fn check_waiting_on_driver_component(&mut self) {
168 if self.bridge.has_driver_component() {
169 return;
170 }
171
172 self.perform_transition(ShutdownState::Stopped);
173 }
174
175 fn check_stopped(&mut self) {
176 self.perform_transition(ShutdownState::WaitingOnDestroy);
177 }
178
179 fn check_waiting_on_destroy(&mut self) {
180 if self.bridge.has_driver_component_controller() {
181 return;
182 }
183
184 self.perform_transition(ShutdownState::Destroyed);
185 }
186
187 fn perform_transition(&mut self, next_state: ShutdownState) {
188 if self.pending_task.is_some() {
189 return;
190 }
191
192 let weak_node = self.bridge.get_weak_node();
193 let action = async move {
194 if let Some(node) = weak_node.upgrade() {
195 let shutdown_intent = {
196 let mut coordinator = node.get_shutdown_coordinator();
197 let shutdown_intent = coordinator.shutdown_intent;
198 match next_state {
199 ShutdownState::WaitingOnChildren => {} ShutdownState::WaitingOnDriver => coordinator.bridge.stop_driver(),
201 ShutdownState::WaitingOnDriverComponent => {
202 coordinator.bridge.stop_driver_component()
203 }
204 ShutdownState::Stopped => {} ShutdownState::WaitingOnDestroy => {
206 if coordinator.bridge.has_driver_component_controller()
207 && !coordinator
208 .bridge
209 .maybe_destroy_driver_component(shutdown_intent)
210 {
211 drop(coordinator.pending_task.take());
214 return;
215 }
216 }
217 ShutdownState::Destroyed => {} _ => panic!("Invalid state for perform_transition"),
219 }
220 shutdown_intent
221 };
222
223 if next_state == ShutdownState::Destroyed {
224 if shutdown_intent != ShutdownIntent::Restart
225 && shutdown_intent != ShutdownIntent::Quarantine
226 {
227 node.finish_shutdown().await;
228 }
229
230 node.schedule_post_shutdown(shutdown_intent);
231 }
232
233 node.get_shutdown_coordinator().update_and_notify_state(next_state);
234 }
235 };
236
237 if let Some(delay_ms) = self.generate_test_delay_ms() {
238 self.pending_task = Some(fasync::Task::local(async move {
239 fasync::Timer::new(zx::MonotonicDuration::from_millis(delay_ms as i64)).await;
240 action.await;
241 }));
242 } else {
243 self.pending_task = Some(fasync::Task::local(async move {
244 action.await;
245 }));
246 }
247 }
248
249 fn generate_test_delay_ms(&mut self) -> Option<u32> {
250 if !self.enable_test_shutdown_delays {
251 return None;
252 }
253 let binding = self.rng_gen.upgrade()?;
254 let mut rng = binding.borrow_mut();
255 if rng.random_range(0..5) == 1 {
256 Some(rng.random_range(MIN_TEST_DELAY_MS..=MAX_TEST_DELAY_MS))
257 } else {
258 None
259 }
260 }
261
262 pub(crate) fn update_and_notify_state(&mut self, state: ShutdownState) {
263 self.node_state = state;
264 if let Some(task) = self.pending_task.take() {
265 drop(task.abort());
266 }
267
268 self.notify_removal_tracker();
269 self.check_node_state();
270 }
271
272 pub(crate) fn notify_removal_tracker(&self) {
273 if let (Some(tracker_weak), Some(id)) = (&self.removal_tracker, self.removal_id)
274 && let Some(tracker) = tracker_weak.upgrade()
275 {
276 tracker.borrow_mut().notify(id, self.node_state, tracker_weak.clone());
277 }
278 }
279
280 pub fn node_state(&self) -> &ShutdownState {
281 &self.node_state
282 }
283
284 pub fn set_removal_tracker(&mut self, tracker: Weak<RefCell<NodeRemovalTracker>>) {
285 if self.removal_tracker.is_none()
286 && let Some(tracker) = tracker.upgrade()
287 {
288 self.removal_id = Some(
289 tracker
290 .borrow_mut()
291 .register_node(self.bridge.get_removal_tracker_info(self.node_state)),
292 );
293
294 self.removal_tracker = Some(Rc::downgrade(&tracker));
295 }
296 }
297
298 pub fn set_shutdown_intent(&mut self, intent: ShutdownIntent) {
299 self.shutdown_intent = intent;
300 }
301
302 pub fn reset_shutdown(&mut self) {
303 self.node_state = ShutdownState::Running;
304 self.shutdown_intent = ShutdownIntent::Removal;
305 }
306
307 pub fn is_shutting_down(&self) -> bool {
308 self.node_state != ShutdownState::Running && self.node_state != ShutdownState::Prestop
309 }
310}