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}