fdf_component/
server.rs

1// Copyright 2024 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 core::ffi::c_void;
6use core::ptr::NonNull;
7use std::num::NonZero;
8use std::ops::ControlFlow;
9use std::sync::OnceLock;
10
11use log::{debug, warn};
12use zx::Status;
13
14use fdf::{Channel, Dispatcher, DispatcherBuilder, DispatcherRef};
15use fidl_fuchsia_driver_framework::DriverRequest;
16
17use fdf::{fdf_handle_t, DriverHandle, Message};
18
19use crate::{Driver, DriverContext};
20use fdf_sys::fdf_dispatcher_get_current_dispatcher;
21use fidl_fuchsia_driver_framework::DriverStartArgs;
22
23/// Implements the lifecycle management of a rust driver, including starting and stopping it
24/// and setting up the rust async dispatcher and logging for the driver to use, and running a
25/// message loop for the driver start and stop messages.
26pub struct DriverServer<T> {
27    server_handle: OnceLock<Channel<[u8]>>,
28    root_dispatcher: DispatcherRef<'static>,
29    driver: OnceLock<T>,
30}
31
32impl<T: Driver> DriverServer<T> {
33    /// Called by the driver host to start the driver.
34    ///
35    /// # Safety
36    ///
37    /// The caller must provide a valid non-zero driver transport channel handle for
38    /// `server_handle`.
39    pub unsafe extern "C" fn initialize(server_handle: fdf_handle_t) -> *mut c_void {
40        // SAFETY: We verify that the pointer returned is non-null, ensuring that this was
41        // called from within a driver context.
42        let root_dispatcher = NonNull::new(unsafe { fdf_dispatcher_get_current_dispatcher() })
43            .expect("Non-null current dispatcher");
44        // SAFETY: We use NonZero::new to verify that we've been given a non-zero
45        // driver handle, and expect that the caller (which is the driver runtime) has given us
46        // a valid driver transport fidl channel.
47        let server_handle = OnceLock::from(unsafe {
48            Channel::from_driver_handle(DriverHandle::new_unchecked(
49                NonZero::new(server_handle).expect("valid driver handle"),
50            ))
51        });
52
53        // SAFETY: the root dispatcher is expected to live as long as this driver is loaded.
54        let root_dispatcher = unsafe { DispatcherRef::from_raw(root_dispatcher) };
55        // We leak the box holding the server so that the driver runtime can take control over the
56        // lifetime of the server object.
57        let server_ptr = Box::into_raw(Box::new(Self {
58            server_handle,
59            root_dispatcher: root_dispatcher.clone(),
60            driver: OnceLock::default(),
61        }));
62
63        // Reconstitute the pointer to the `DriverServer` as a mut reference to use it in the main
64        // loop.
65        // SAFETY: We are the exclusive owner of the object until we drop the server handle,
66        // triggering the driver host to call `destroy`.
67        let server = unsafe { &mut *server_ptr };
68
69        // Build a new dispatcher that we can have spin on a fuchsia-async executor main loop
70        // to act as a reactor for non-driver events.
71        let rust_async_dispatcher = DispatcherBuilder::new()
72            .name("fuchsia-async")
73            .allow_thread_blocking()
74            .create_released()
75            .expect("failure creating blocking dispatcher for rust async");
76        // Post the task to the dispatcher that will run the fuchsia-async loop, and have it run
77        // the server's message loop waiting for start and stop messages from the driver host.
78        rust_async_dispatcher
79            .post_task_sync(move |status| {
80                // bail immediately if we were somehow cancelled before we started
81                let Status::OK = status else { return };
82                Dispatcher::override_current(root_dispatcher.clone(), || {
83                    // create and run a fuchsia-async executor, giving it the "root" dispatcher to
84                    // actually execute driver tasks on, as this thread will be effectively blocked
85                    // by the reactor loop.
86                    let mut executor = fuchsia_async::LocalExecutor::new();
87                    executor.run_singlethreaded(async move {
88                        server.message_loop(root_dispatcher).await;
89                        // take the server handle so it can drop after the async block is done,
90                        // which will signal to the driver host that the driver has finished
91                        // shutdown, so that we are can guarantee that when `destroy` is called, we
92                        // are not still using `server`.
93                        server.server_handle.take()
94                    });
95                });
96            })
97            .expect("failure spawning main event loop for rust async dispatch");
98
99        // Take the pointer of the server object to use as the identifier for the server to the
100        // driver runtime. It uses this as an opaque identifier and expects no particular layout of
101        // the object pointed to, and we use it to free the box at unload in `Self::destroy`.
102        server_ptr.cast()
103    }
104
105    /// Called by the driver host after shutting down a driver and once the handle passed to
106    /// [`Self::initialize`] is dropped.
107    ///
108    /// # Safety
109    ///
110    /// This must only be called after the handle provided to [`Self::initialize`] has been
111    /// dropped, which indicates that the main event loop of the driver lifecycle has ended.
112    pub unsafe extern "C" fn destroy(obj: *mut c_void) {
113        let obj: *mut Self = obj.cast();
114        // SAFETY: We built this object in `initialize` and gave ownership of its
115        // lifetime to the driver framework, which is now giving it to us to free.
116        unsafe { drop(Box::from_raw(obj)) }
117    }
118
119    /// Implements the main message loop for handling start and stop messages from rust
120    /// driver host and passing them on to the implementation of [`Driver`] we contain.
121    async fn message_loop(&mut self, dispatcher: DispatcherRef<'_>) {
122        loop {
123            let server_handle_lock = self.server_handle.get();
124            let Some(server_handle) = server_handle_lock else {
125                panic!("driver already shut down while message loop was running")
126            };
127            match server_handle.read_bytes(dispatcher.clone()).await {
128                Ok(Some(message)) => {
129                    if let ControlFlow::Break(_) = self.handle_message(message).await {
130                        // driver shut down or failed to start, exit message loop
131                        return;
132                    }
133                }
134                Ok(None) => panic!("unexpected empty message on server channel"),
135                Err(status @ Status::PEER_CLOSED) | Err(status @ Status::UNAVAILABLE) => {
136                    warn!("Driver server channel closed before a stop message with status {status}, exiting main loop early but stop() will not be called.");
137                    return;
138                }
139                Err(e) => panic!("unexpected error on server channel {e}"),
140            }
141        }
142    }
143
144    /// Handles the start message by initializing logging and calling the [`Driver::start`] with
145    /// a constructed [`DriverContext`].
146    ///
147    /// # Panics
148    ///
149    /// This method panics if the start message has already been received.
150    async fn handle_start(&self, start_args: DriverStartArgs) -> Result<(), Status> {
151        let context = DriverContext::new(self.root_dispatcher.clone(), start_args)?;
152        context.start_logging(T::NAME)?;
153
154        log::debug!("driver starting");
155
156        let driver = T::start(context).await?;
157        self.driver.set(driver).map_err(|_| ()).expect("Driver received start message twice");
158        Ok(())
159    }
160
161    async fn handle_stop(&mut self) {
162        log::debug!("driver stopping");
163        self.driver
164            .take()
165            .expect("received stop message more than once or without successfully starting")
166            .stop()
167            .await;
168    }
169
170    /// Dispatches messages from the driver host to the appropriate implementation.
171    ///
172    /// # Panics
173    ///
174    /// This method panics if the messages are received out of order somehow (two start messages,
175    /// stop before start, etc).
176    async fn handle_message(&mut self, message: Message<[u8]>) -> ControlFlow<()> {
177        let (_, request) = DriverRequest::read_from_message(message).unwrap();
178        match request {
179            DriverRequest::Start { start_args, responder } => {
180                let res = self.handle_start(start_args).await.map_err(Status::into_raw);
181                let Some(server_handle) = self.server_handle.get() else {
182                    panic!("driver shutting down before it was finished starting")
183                };
184                responder.send_response(server_handle, res).unwrap();
185                if res.is_ok() {
186                    ControlFlow::Continue(())
187                } else {
188                    debug!("driver failed to start, exiting main loop");
189                    ControlFlow::Break(())
190                }
191            }
192            DriverRequest::Stop {} => {
193                self.handle_stop().await;
194                ControlFlow::Break(())
195            }
196            _ => panic!("Unknown message on server channel"),
197        }
198    }
199}
200
201#[cfg(test)]
202mod tests {
203    use super::*;
204
205    use fdf::{CurrentDispatcher, OnDispatcher};
206    use fdf_env::test::spawn_in_driver;
207    use fidl_next_fuchsia_driver_framework::DriverClientHandler;
208    use zx::Status;
209
210    use fdf::Channel;
211    use fidl_next::{Client, ClientEnd};
212    use fidl_next_fuchsia_driver_framework::{DriverClientSender, DriverStartRequest};
213
214    #[derive(Default)]
215    struct TestDriver {
216        _not_empty: bool,
217    }
218
219    impl Driver for TestDriver {
220        const NAME: &str = "test_driver";
221
222        async fn start(context: DriverContext) -> Result<Self, Status> {
223            let DriverContext { root_dispatcher, start_args, .. } = context;
224            println!("created new test driver on dispatcher: {root_dispatcher:?}");
225            println!("driver start message: {start_args:?}");
226            Ok(Self::default())
227        }
228        async fn stop(&self) {
229            println!("driver stop message");
230        }
231    }
232
233    crate::driver_register!(TestDriver);
234
235    struct DriverClient;
236    impl DriverClientHandler<fdf_fidl::DriverChannel> for DriverClient {}
237
238    #[test]
239    fn register_driver() {
240        assert_eq!(__fuchsia_driver_registration__.version, 1);
241        let initialize_func = __fuchsia_driver_registration__.v1.initialize.expect("initializer");
242        let destroy_func = __fuchsia_driver_registration__.v1.destroy.expect("destroy function");
243
244        let (server_chan, client_chan) = Channel::<[fidl_next::Chunk]>::create();
245
246        let (client_exit_tx, client_exit_rx) = futures::channel::oneshot::channel();
247        spawn_in_driver("driver registration", async move {
248            let client_end = ClientEnd::from_untyped(fdf_fidl::DriverChannel::new(client_chan));
249            let mut client = Client::new(client_end);
250            let client_sender = client.sender().clone();
251
252            CurrentDispatcher
253                .spawn_task(async move {
254                    client.run(DriverClient).await.unwrap_err();
255                    client_exit_tx.send(()).unwrap();
256                })
257                .unwrap();
258
259            let channel_handle = server_chan.into_driver_handle().into_raw().get();
260            let driver_server = unsafe { initialize_func(channel_handle) } as usize;
261            assert_ne!(driver_server, 0);
262            let mut start_request = DriverStartRequest {
263                start_args: fidl_next_fuchsia_driver_framework::DriverStartArgs {
264                    node: None,
265                    symbols: None,
266                    url: None,
267                    program: None,
268                    incoming: None,
269                    outgoing_dir: None,
270                    config: None,
271                    node_name: None,
272                    node_properties: None,
273                    node_offers: None,
274                    node_token: None,
275                    node_properties_2: None,
276                },
277            };
278
279            let mut start_res = client_sender.start(&mut start_request).unwrap().await.unwrap();
280            start_res.decode().unwrap().unwrap();
281
282            client_sender.stop().unwrap().await.unwrap();
283            client_exit_rx.await.unwrap();
284
285            unsafe {
286                destroy_func(driver_server as *mut c_void);
287            }
288        })
289    }
290}