Skip to main content

fdf_core/
shutdown_observer.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
5//! Utility struct for constructing a shutdown observer compatible with rust callbacks.
6
7use fdf_sys::*;
8
9use core::ptr::NonNull;
10
11use crate::dispatcher::{DriverDispatcherRef, ShutdownObserverFn};
12
13/// A shutdown observer for [`fdf_dispatcher_create`] that can call any kind of callback instead of
14/// just a C-compatible function when a dispatcher is shutdown.
15///
16/// # Safety
17///
18/// This object relies on a specific layout to allow it to be cast between a
19/// `*mut fdf_dispatcher_shutdown_observer` and a `*mut ShutdownObserver`. To that end,
20/// it is important that this struct stay both `#[repr(C)]` and that `observer` be its first member.
21#[repr(C)]
22pub struct ShutdownObserver {
23    observer: fdf_dispatcher_shutdown_observer,
24    shutdown_fn: Box<dyn ShutdownObserverFn>,
25}
26
27impl ShutdownObserver {
28    /// Creates a new [`ShutdownObserver`] with `f` as the callback to run when a dispatcher
29    /// finishes shutting down.
30    pub fn new<F: ShutdownObserverFn>(f: F) -> Self {
31        let shutdown_fn = Box::new(f);
32        Self {
33            observer: fdf_dispatcher_shutdown_observer { handler: Some(Self::handler) },
34            shutdown_fn,
35        }
36    }
37
38    /// Turns this object into a stable pointer suitable for passing to [`fdf_dispatcher_create`]
39    /// by wrapping it in a [`Box`] and leaking it to be reconstituded by [`Self::handler`] when
40    /// the dispatcher is shut down.
41    pub fn into_ptr(self) -> *mut fdf_dispatcher_shutdown_observer {
42        // Note: this relies on the assumption that `self.observer` is at the beginning of the
43        // struct.
44        Box::leak(Box::new(self)) as *mut _ as *mut _
45    }
46
47    /// The callback that is registered with the dispatcher that will be called when the dispatcher
48    /// is shut down.
49    ///
50    /// # Safety
51    ///
52    /// This function should only ever be called by the driver runtime at dispatcher shutdown
53    /// time, must only ever be called once for any given [`ShutdownObserver`] object, and
54    /// that [`ShutdownObserver`] object must have previously been made into a pointer by
55    /// [`Self::into_ptr`].
56    unsafe extern "C" fn handler(
57        dispatcher: *mut fdf_dispatcher_t,
58        observer: *mut fdf_dispatcher_shutdown_observer_t,
59    ) {
60        // SAFETY: The driver framework promises to only call this function once, so we can
61        // safely take ownership of the [`Box`] and deallocate it when this function ends.
62        let observer = unsafe { Box::from_raw(observer as *mut ShutdownObserver) };
63        // SAFETY: `dispatcher` is the dispatcher being shut down, so it can't be non-null.
64        let dispatcher_ref =
65            unsafe { DriverDispatcherRef::from_raw(NonNull::new_unchecked(dispatcher)) };
66        (observer.shutdown_fn)(dispatcher_ref);
67        // SAFETY: we only shutdown the dispatcher when the dispatcher is dropped, and we only ever
68        // instantiate one owned copy of `Dispatcher` for a given dispatcher.
69        unsafe { fdf_dispatcher_destroy(dispatcher) };
70    }
71}