Skip to main content

driver_manager_shutdown/
node_shutdown_coordinator.rs

1// Copyright 2026 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use 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
16// The range of test delay time in milliseconds.
17const 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 => {} // No action before state change
200                        ShutdownState::WaitingOnDriver => coordinator.bridge.stop_driver(),
201                        ShutdownState::WaitingOnDriverComponent => {
202                            coordinator.bridge.stop_driver_component()
203                        }
204                        ShutdownState::Stopped => {} // No action before state change
205                        ShutdownState::WaitingOnDestroy => {
206                            if coordinator.bridge.has_driver_component_controller()
207                                && !coordinator
208                                    .bridge
209                                    .maybe_destroy_driver_component(shutdown_intent)
210                            {
211                                // Not ready to transition to WaitingOnDestroy if we haven't sent
212                                // the destroy request yet.
213                                drop(coordinator.pending_task.take());
214                                return;
215                            }
216                        }
217                        ShutdownState::Destroyed => {} // No action before state change
218                        _ => 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}