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}