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