Skip to main content

fdf_env/
lib.rs

1// Copyright 2025 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//! Safe bindings for driver runtime environment.
6
7#![deny(missing_docs)]
8
9use fdf_sys::*;
10use zx::sys::zx_duration_mono_t;
11
12use core::ffi;
13use core::marker::PhantomData;
14use core::ptr::{NonNull, null_mut};
15
16use zx::Status;
17
18use fdf_core::dispatcher::{Dispatcher, DispatcherBuilder, DriverDispatcherRef};
19use fdf_core::shutdown_observer::ShutdownObserver;
20
21pub mod test;
22
23/// Create the dispatcher as configured by this object. This must be called from a
24/// thread managed by the driver runtime. The dispatcher returned is owned by the caller,
25/// and will initiate asynchronous shutdown when the object is dropped unless
26/// [`Dispatcher::release`] is called on it to convert it into an unowned [`DispatcherRef`].
27fn create_with_driver<'a>(
28    dispatcher: DispatcherBuilder,
29    driver: DriverRefTypeErased<'a>,
30) -> Result<Dispatcher, Status> {
31    let mut out_dispatcher = null_mut();
32    let owner = driver.0;
33    let options = dispatcher.options;
34    let name = dispatcher.name.as_ptr() as *mut ffi::c_char;
35    let name_len = dispatcher.name.len();
36    let scheduler_role = dispatcher.scheduler_role.as_ptr() as *mut ffi::c_char;
37    let scheduler_role_len = dispatcher.scheduler_role.len();
38    let observer =
39        ShutdownObserver::new(dispatcher.shutdown_observer.unwrap_or_else(|| Box::new(|_| {})))
40            .into_ptr();
41    // SAFETY: all arguments point to memory that will be available for the duration
42    // of the call, except `observer`, which will be available until it is unallocated
43    // by the dispatcher exit handler.
44    Status::ok(unsafe {
45        fdf_env_dispatcher_create_with_owner(
46            owner,
47            options,
48            name,
49            name_len,
50            scheduler_role,
51            scheduler_role_len,
52            observer,
53            &mut out_dispatcher,
54        )
55    })?;
56    // SAFETY: `out_dispatcher` is valid by construction if `fdf_dispatcher_create` returns
57    // ZX_OK.
58    Ok(unsafe { Dispatcher::from_raw(NonNull::new_unchecked(out_dispatcher)) })
59}
60
61/// A marker trait for a function that can be used as a driver shutdown observer with
62/// [`Driver::shutdown`].
63pub trait DriverShutdownObserverFn<T: 'static>:
64    FnOnce(DriverRef<'static, T>) + Send + Sync + 'static
65{
66}
67impl<T, U: 'static> DriverShutdownObserverFn<U> for T where
68    T: FnOnce(DriverRef<'static, U>) + Send + Sync + 'static
69{
70}
71
72/// A shutdown observer for [`fdf_dispatcher_create`] that can call any kind of callback instead of
73/// just a C-compatible function when a dispatcher is shutdown.
74///
75/// # Safety
76///
77/// This object relies on a specific layout to allow it to be cast between a
78/// `*mut fdf_dispatcher_shutdown_observer` and a `*mut ShutdownObserver`. To that end,
79/// it is important that this struct stay both `#[repr(C)]` and that `observer` be its first member.
80#[repr(C)]
81struct DriverShutdownObserver<T: 'static> {
82    observer: fdf_env_driver_shutdown_observer,
83    shutdown_fn: Box<dyn DriverShutdownObserverFn<T>>,
84    driver: Driver<T>,
85}
86
87impl<T: 'static> DriverShutdownObserver<T> {
88    /// Creates a new [`ShutdownObserver`] with `f` as the callback to run when a dispatcher
89    /// finishes shutting down.
90    fn new<F: DriverShutdownObserverFn<T>>(driver: Driver<T>, f: F) -> Self {
91        let shutdown_fn = Box::new(f);
92        Self {
93            observer: fdf_env_driver_shutdown_observer { handler: Some(Self::handler) },
94            shutdown_fn,
95            driver,
96        }
97    }
98
99    /// Begins the driver shutdown procedure.
100    /// Turns this object into a stable pointer suitable for passing to
101    /// [`fdf_env_shutdown_dispatchers_async`] by wrapping it in a [`Box`] and leaking it
102    /// to be reconstituded by [`Self::handler`] when the dispatcher is shut down.
103    fn begin(self) -> Result<(), Status> {
104        let driver = self.driver.inner.as_ptr() as *const _;
105        // Note: this relies on the assumption that `self.observer` is at the beginning of the
106        // struct.
107        let this = Box::into_raw(Box::new(self)) as *mut _;
108        // SAFETY: driver is owned by the driver framework and will be kept alive until the handler
109        // callback is triggered
110        if let Err(e) = Status::ok(unsafe { fdf_env_shutdown_dispatchers_async(driver, this) }) {
111            // SAFETY: The framework didn't actually take ownership of the object if the call
112            // fails, so we can recover it to avoid leaking.
113            let _ = unsafe { Box::from_raw(this as *mut DriverShutdownObserver<T>) };
114            return Err(e);
115        }
116        Ok(())
117    }
118
119    /// The callback that is registered with the driver that will be called when the driver
120    /// is shut down.
121    ///
122    /// # Safety
123    ///
124    /// This function should only ever be called by the driver runtime at dispatcher shutdown
125    /// time, must only ever be called once for any given [`ShutdownObserver`] object, and
126    /// that [`ShutdownObserver`] object must have previously been made into a pointer by
127    /// [`Self::into_ptr`].
128    unsafe extern "C" fn handler(
129        driver: *const ffi::c_void,
130        observer: *mut fdf_env_driver_shutdown_observer_t,
131    ) {
132        // SAFETY: The driver framework promises to only call this function once, so we can
133        // safely take ownership of the [`Box`] and deallocate it when this function ends.
134        let observer = unsafe { Box::from_raw(observer as *mut DriverShutdownObserver<T>) };
135        (observer.shutdown_fn)(DriverRef(driver as *const T, PhantomData));
136    }
137}
138
139/// An owned handle to a Driver instance that can be used to create initial dispatchers.
140#[derive(Debug)]
141pub struct Driver<T> {
142    pub(crate) inner: NonNull<T>,
143    shutdown_triggered: bool,
144}
145
146/// An unowned handle to the driver that is returned through certain environment APIs like
147/// |get_driver_on_thread_koid|.
148pub struct UnownedDriver {
149    inner: *const ffi::c_void,
150}
151
152/// SAFETY: This inner pointer is movable across threads.
153unsafe impl<T: Send> Send for Driver<T> {}
154
155impl<T: 'static> Driver<T> {
156    /// Constructs a dispatcher from the given builder on this driver. Note that this dispatcher
157    /// cannot outlive the driver and is only capable of being stopped by shutting down the driver.
158    /// It is meant to be created to serve as the initial or default dispatcher for a driver.
159    ///
160    /// The caller should make sure that the dispatcher is released so the driver runtime will
161    /// manage shutting it down, but that may be done differently in test contexts so it does not
162    /// force it.
163    pub fn new_dispatcher(&self, dispatcher: DispatcherBuilder) -> Result<Dispatcher, Status> {
164        create_with_driver(dispatcher, self.as_ref_type_erased())
165    }
166
167    /// Run a closure in the context of a driver.
168    pub fn enter<R>(&mut self, f: impl FnOnce() -> R) -> R {
169        unsafe { fdf_env_register_driver_entry(self.inner.as_ptr() as *const _) };
170        let res = f();
171        unsafe { fdf_env_register_driver_exit() };
172        res
173    }
174
175    /// Adds an allowed scheduler role to the driver
176    pub fn add_allowed_scheduler_role(&self, scheduler_role: &str) {
177        let driver_ptr = self.inner.as_ptr() as *const _;
178        let scheduler_role_ptr = scheduler_role.as_ptr() as *mut ffi::c_char;
179        let scheduler_role_len = scheduler_role.len();
180        unsafe {
181            fdf_env_add_allowed_scheduler_role_for_driver(
182                driver_ptr,
183                scheduler_role_ptr,
184                scheduler_role_len,
185            )
186        };
187    }
188
189    /// Registers a callback which is triggered whenever the runtime needs to be resumed.
190    /// Returns a registration handle that unregisters and frees the requester when dropped.
191    pub fn register_resume_requester(
192        &self,
193        requester: ResumeRequester,
194    ) -> ResumeRequesterRegistration {
195        let driver_ptr = self.inner.as_ptr() as *const _;
196        let requester_ptr = requester.into_ptr();
197
198        // SAFETY: requester_ptr is used by the driver runtime as a callback function.
199        // The driver runtime does not manage this object's lifetime. driver_ptr is not modified
200        // by the runtime.
201        unsafe {
202            fdf_sys::fdf_env_register_resume_requester(driver_ptr, requester_ptr);
203        }
204        ResumeRequesterRegistration { driver_ptr, requester_ptr }
205    }
206
207    /// Asynchronously suspends the dispatchers owned by the driver.
208    pub fn driver_suspend(&self, completer: SuspendCompleter) {
209        unsafe {
210            fdf_sys::fdf_env_driver_suspend(self.inner.as_ptr() as *const _, completer.into_ptr());
211        }
212    }
213
214    /// Resumes the dispatchers owned by the driver.
215    pub fn driver_resume(&self) {
216        unsafe {
217            fdf_sys::fdf_env_driver_resume(self.inner.as_ptr() as *const _);
218        }
219    }
220
221    /// Asynchronously shuts down all dispatchers owned by |driver|.
222    /// |f| will be called once shutdown completes. This is guaranteed to be
223    /// after all the dispatcher's shutdown observers have been called, and will be running
224    /// on the thread of the final dispatcher which has been shutdown.
225    pub fn shutdown<F: DriverShutdownObserverFn<T>>(mut self, f: F) {
226        self.shutdown_triggered = true;
227        // It should be impossible for this to fail as we ensure we are the only caller of this
228        // API, so it cannot be triggered twice nor before the driver has been registered with the
229        // framework.
230        DriverShutdownObserver::new(self, f)
231            .begin()
232            .expect("Unexpectedly failed start shutdown procedure")
233    }
234
235    /// Create a reference to a driver without ownership. The returned reference lacks the ability
236    /// to perform most actions available to the owner of the driver, therefore it doesn't need to
237    /// have it's lifetime tracked closely.
238    fn as_ref_type_erased<'a>(&'a self) -> DriverRefTypeErased<'a> {
239        DriverRefTypeErased(self.inner.as_ptr() as *const _, PhantomData)
240    }
241
242    /// Releases ownership of this driver instance, allowing it to be shut down when the runtime
243    /// shuts down.
244    pub fn release(self) -> DriverRef<'static, T> {
245        DriverRef(self.inner.as_ptr() as *const _, PhantomData)
246    }
247}
248
249impl<T> Drop for Driver<T> {
250    fn drop(&mut self) {
251        assert!(self.shutdown_triggered, "Cannot drop driver, must call shutdown method instead");
252    }
253}
254
255impl<T> PartialEq<UnownedDriver> for Driver<T> {
256    fn eq(&self, other: &UnownedDriver) -> bool {
257        self.inner.as_ptr() as *const _ == other.inner
258    }
259}
260
261// Note that inner type is not guaranteed to not be null.
262#[derive(Clone, Copy, PartialEq)]
263struct DriverRefTypeErased<'a>(*const ffi::c_void, PhantomData<&'a u32>);
264
265impl Default for DriverRefTypeErased<'_> {
266    fn default() -> Self {
267        DriverRefTypeErased(std::ptr::null(), PhantomData)
268    }
269}
270
271/// A lifetime-bound reference to a driver handle.
272pub struct DriverRef<'a, T>(pub *const T, PhantomData<&'a Driver<T>>);
273
274/// A marker trait for a function type that can be used as a stall scanner.
275pub trait StallScannerFn: Fn(zx_duration_mono_t) + Send + Sync + 'static {}
276impl<T> StallScannerFn for T where T: Fn(zx_duration_mono_t) + Send + Sync + 'static {}
277
278/// A stall scanner for [`fdf_env_register_stall_scanner`] that can call any kind of callback instead of
279/// just a C-compatible function when a dispatcher is shutdown.
280///
281/// # Safety
282///
283/// This object relies on a specific layout to allow it to be cast between a
284/// `*mut fdf_env_stall_scanner` and a `*mut StallScanner`. To that end,
285/// it is important that this struct stay both `#[repr(C)]` and that `scanner` be its first member.
286#[repr(C)]
287#[doc(hidden)]
288pub struct StallScanner {
289    scanner: fdf_env_stall_scanner,
290    begin_fn: Box<dyn StallScannerFn>,
291}
292
293impl StallScanner {
294    /// Creates a new [`StallScanner`] with `f` as the callback to run when a dispatcher
295    /// finishes shutting down.
296    pub fn new<F: StallScannerFn>(f: F) -> Self {
297        let begin_fn = Box::new(f);
298        Self { scanner: fdf_env_stall_scanner { handler: Some(Self::handler) }, begin_fn }
299    }
300
301    /// Turns this object into a stable pointer suitable for passing to
302    /// [`fdf_env_register_stall_scanner`] by wrapping it in a [`Box`] and leaking it to be reconstituded
303    /// by [`Self::handler`] when the scanner is triggered.
304    pub fn into_ptr(self) -> *mut fdf_env_stall_scanner {
305        // Note: this relies on the assumption that `self.scanner` is at the beginning of the
306        // struct.
307        Box::leak(Box::new(self)) as *mut _ as *mut _
308    }
309
310    /// The callback that is registered with the dispatcher that will be called when the stall
311    /// scanner should begin a scan.
312    ///
313    /// # Safety
314    ///
315    /// The [`StallScanner`] object must have previously been made into a pointer by
316    /// [`Self::into_ptr`].
317    unsafe extern "C" fn handler(
318        scanner: *mut fdf_env_stall_scanner,
319        duration: zx_duration_mono_t,
320    ) {
321        let scanner = scanner as *mut StallScanner;
322
323        unsafe {
324            ((*scanner).begin_fn)(duration);
325        }
326    }
327}
328
329/// A marker trait for a function type that can be used as a resume requester.
330pub trait ResumeRequesterFn: Fn() -> Result<(), Status> + Send + Sync + 'static {}
331impl<T> ResumeRequesterFn for T where T: Fn() -> Result<(), Status> + Send + Sync + 'static {}
332
333/// A resume requester for [`fdf_env_register_resume_requester`] that can call any kind of callback.
334///
335/// # Safety
336///
337/// This object relies on a specific layout to allow it to be cast between a
338/// `*mut fdf_env_resume_requester_t` and a `*mut ResumeRequester`. To that end,
339/// it is important that this struct stay both `#[repr(C)]` and that `requester` be its first member.
340#[repr(C)]
341pub struct ResumeRequester {
342    /// The underlying C structure.
343    pub requester: fdf_env_resume_requester_t,
344    resume_fn: Box<dyn ResumeRequesterFn>,
345}
346
347impl ResumeRequester {
348    /// Creates a new [`ResumeRequester`] with `f` as the callback to run when the runtime needs to be resumed.
349    pub fn new<F: ResumeRequesterFn>(f: F) -> Self {
350        let resume_fn = Box::new(f);
351        Self { requester: fdf_env_resume_requester_t { handler: Some(Self::handler) }, resume_fn }
352    }
353
354    /// Turns this object into a stable pointer suitable for passing to
355    /// [`fdf_env_register_resume_requester`] by wrapping it in a [`Box`] and leaking it to be reconstituded
356    /// by [`Self::handler`] when the runtime needs to be resumed.
357    pub fn into_ptr(self) -> *mut fdf_env_resume_requester_t {
358        Box::leak(Box::new(self)) as *mut _ as *mut _
359    }
360
361    /// The callback that is registered with the dispatcher that will be called when the runtime
362    /// needs to be resumed.
363    ///
364    /// # Safety
365    ///
366    /// The [`ResumeRequester`] object must have previously been made into a pointer by
367    /// [`Self::into_ptr`].
368    unsafe extern "C" fn handler(requester: *mut fdf_env_resume_requester_t) -> i32 {
369        let requester = requester as *mut ResumeRequester;
370        unsafe {
371            match ((*requester).resume_fn)() {
372                Ok(()) => 0,
373                Err(e) => e.into_raw(),
374            }
375        }
376    }
377}
378
379/// A marker trait for a function type that can be used as a suspend completer.
380pub trait SuspendCompleterFn: FnOnce() + Send + Sync + 'static {}
381impl<T> SuspendCompleterFn for T where T: FnOnce() + Send + Sync + 'static {}
382
383/// A suspend completer for [`fdf_env_driver_suspend`] that can call any kind of callback.
384///
385/// # Safety
386///
387/// This object relies on a specific layout to allow it to be cast between a
388/// `*mut fdf_env_suspend_completer_t` and a `*mut SuspendCompleter`. To that end,
389/// it is important that this struct stay both `#[repr(C)]` and that `completer` be its first member.
390#[repr(C)]
391pub struct SuspendCompleter {
392    completer: fdf_env_suspend_completer_t,
393    complete_fn: Box<dyn SuspendCompleterFn>,
394}
395
396impl SuspendCompleter {
397    /// Creates a new [`SuspendCompleter`] with `f` as the callback to run when the runtime finishes suspending.
398    pub fn new<F: SuspendCompleterFn>(f: F) -> Self {
399        let complete_fn = Box::new(f);
400        Self {
401            completer: fdf_env_suspend_completer_t { handler: Some(Self::handler) },
402            complete_fn,
403        }
404    }
405
406    /// Turns this object into a stable pointer suitable for passing to
407    /// [`fdf_env_driver_suspend`] by wrapping it in a [`Box`] and leaking it to be reconstituded
408    /// by [`Self::handler`] when the runtime finishes suspending.
409    pub fn into_ptr(self) -> *mut fdf_env_suspend_completer_t {
410        Box::leak(Box::new(self)) as *mut _ as *mut _
411    }
412
413    /// The callback that is registered with the dispatcher that will be called when the runtime
414    /// finishes suspending.
415    ///
416    /// # Safety
417    ///
418    /// The [`SuspendCompleter`] object must have previously been made into a pointer by
419    /// [`Self::into_ptr`].
420    unsafe extern "C" fn handler(completer: *mut fdf_env_suspend_completer_t) {
421        let completer = completer as *mut SuspendCompleter;
422        unsafe {
423            let completer = Box::from_raw(completer);
424            (completer.complete_fn)();
425        }
426    }
427}
428
429/// The driver runtime environment
430pub struct Environment;
431
432impl Environment {
433    /// Whether the environment should enforce scheduler roles. Used with [`Self::start`].
434    pub const ENFORCE_ALLOWED_SCHEDULER_ROLES: u32 = 1;
435    /// Whether the environment should dynamically spawn threads on-demand for sync call dispatchers.
436    /// Used with [`Self::start`].
437    pub const DYNAMIC_THREAD_SPAWNING: u32 = 2;
438
439    /// Start the driver runtime. This sets up the initial thread that the dispatchers run on.
440    pub fn start(options: u32) -> Result<Environment, Status> {
441        // SAFETY: calling fdf_env_start, which does not have any soundness
442        // concerns for rust code. It may be called multiple times without any problems.
443        Status::ok(unsafe { fdf_env_start(options) })?;
444        Ok(Self)
445    }
446
447    /// Creates a new driver. It is expected that the driver passed in is a leaked pointer which
448    /// will only be recovered by triggering the shutdown method on the driver.
449    ///
450    /// # Panics
451    ///
452    /// This method will panic if |driver| is not null.
453    pub fn new_driver<T>(&self, driver: *const T) -> Driver<T> {
454        // We cast to *mut because there is not equivlaent version of NonNull for *const T.
455        Driver {
456            inner: NonNull::new(driver as *mut _).expect("driver must not be null"),
457            shutdown_triggered: false,
458        }
459    }
460
461    // TODO: Consider tracking all drivers and providing a method to shutdown all outstanding
462    // drivers and block until they've all finished shutting down.
463
464    /// Returns whether the current thread is managed by the driver runtime or not.
465    fn current_thread_managed_by_driver_runtime() -> bool {
466        // Safety: Calling fdf_dispatcher_get_current_dispatcher from any thread is safe. Because
467        // we are not actually using the dispatcher, we don't need to worry about it's lifetime.
468        !unsafe { fdf_dispatcher_get_current_dispatcher().is_null() }
469    }
470
471    /// Resets the driver runtime to zero threads. This may only be called when there are no
472    /// existing dispatchers.
473    ///
474    /// # Panics
475    ///
476    /// This method should not be called from a thread managed by the driver runtime,
477    /// such as from tasks or ChannelRead callbacks.
478    pub fn reset(&self) {
479        assert!(
480            !Self::current_thread_managed_by_driver_runtime(),
481            "reset must be called from a thread not managed by the driver runtime"
482        );
483        // SAFETY: calling fdf_env_reset, which does not have any soundness
484        // concerns for rust code. It may be called multiple times without any problems.
485        unsafe { fdf_env_reset() };
486    }
487
488    /// Destroys all dispatchers in the process and blocks the current thread
489    /// until each runtime dispatcher in the process is observed to have been destroyed.
490    ///
491    /// This should only be used called after all drivers have been shutdown.
492    ///
493    /// # Panics
494    ///
495    /// This method should not be called from a thread managed by the driver runtime,
496    /// such as from tasks or ChannelRead callbacks.
497    pub fn destroy_all_dispatchers(&self) {
498        assert!(
499            !Self::current_thread_managed_by_driver_runtime(),
500            "destroy_all_dispatchers must be called from a thread not managed by the driver runtime"
501        );
502        unsafe { fdf_env_destroy_all_dispatchers() };
503    }
504
505    /// Returns whether the dispatcher has any queued tasks.
506    pub fn dispatcher_has_queued_tasks(&self, dispatcher: DriverDispatcherRef<'_>) -> bool {
507        unsafe {
508            fdf_env_dispatcher_has_queued_tasks(fdf_core::dispatcher_ptr(&dispatcher).as_ptr())
509        }
510    }
511
512    /// Returns the current maximum number of threads which will be spawned for thread pool associated
513    /// with the given scheduler role.
514    ///
515    /// |scheduler_role| is the name of the role which is passed when creating dispatchers.
516    pub fn get_thread_limit(&self, scheduler_role: &str) -> u32 {
517        let scheduler_role_ptr = scheduler_role.as_ptr() as *mut ffi::c_char;
518        let scheduler_role_len = scheduler_role.len();
519        unsafe { fdf_env_get_thread_limit(scheduler_role_ptr, scheduler_role_len) }
520    }
521
522    /// Sets the number of threads which will be spawned for thread pool associated with the given
523    /// scheduler role. It cannot shrink the limit less to a value lower than the current number of
524    /// threads in the thread pool.
525    ///
526    /// |scheduler_role| is the name of the role which is passed when creating dispatchers.
527    /// |max_threads| is the number of threads to use as new limit.
528    pub fn set_thread_limit(&self, scheduler_role: &str, max_threads: u32) -> Result<(), Status> {
529        let scheduler_role_ptr = scheduler_role.as_ptr() as *mut ffi::c_char;
530        let scheduler_role_len = scheduler_role.len();
531        Status::ok(unsafe {
532            fdf_env_set_thread_limit(scheduler_role_ptr, scheduler_role_len, max_threads)
533        })
534    }
535    /// Returns the currently set options for the scheduler role as a uint32_t bitmask.
536    ///
537    /// |scheduler_role| is the name of the role which is passed when creating dispatchers.
538    pub fn get_scheduler_role_opts(&self, scheduler_role: &str) -> u32 {
539        let scheduler_role_ptr = scheduler_role.as_ptr() as *mut ffi::c_char;
540        let scheduler_role_len = scheduler_role.len();
541        unsafe { fdf_env_get_scheduler_role_opts(scheduler_role_ptr, scheduler_role_len) }
542    }
543
544    /// When used with [`Self::set_scheduler_role_opts`], this will not allow any dispatchers on the
545    /// scheduler role to be created with `FDF_DISPATCHER_OPTION_ALLOW_SYNC_CALLS`.
546    pub const SCHEDULER_ROLE_OPTION_NO_SYNC_CALLS: u32 = FDF_SCHEDULER_ROLE_OPTION_NO_SYNC_CALLS;
547
548    /// Sets the options for the given scheduler role. This can be used to enforce restrictions
549    /// on the kinds of dispatchers that can be created on this scheduler role.
550    ///
551    /// |scheduler_role| is the name of the role which is passed when creating dispatchers.
552    /// |options| is the new options for the scheduler role.
553    ///
554    /// # Errors
555    ///
556    /// [`Status::INVALID_ARGS`]: |options| contains unknown or invalid options.
557    /// [`Status::ERR_NOT_SUPPORTED`]: |options| contains an option that wouldn't allow a dispatcher
558    /// that already exists on this scheduler role.
559    pub fn set_scheduler_role_opts(
560        &self,
561        scheduler_role: &str,
562        options: u32,
563    ) -> Result<(), Status> {
564        let scheduler_role_ptr = scheduler_role.as_ptr() as *mut ffi::c_char;
565        let scheduler_role_len = scheduler_role.len();
566        Status::ok(unsafe {
567            fdf_env_set_scheduler_role_opts(scheduler_role_ptr, scheduler_role_len, options)
568        })
569    }
570
571    /// Gets the driver currently running on the thread identified by |thread_koid|, if the thread
572    /// is running on this driver host with a driver.
573    pub fn get_driver_on_thread_koid(&self, thread_koid: zx::Koid) -> Option<UnownedDriver> {
574        let mut driver = std::ptr::null();
575        unsafe {
576            Status::ok(fdf_env_get_driver_on_tid(thread_koid.raw_koid(), &mut driver)).ok()?;
577        }
578        if driver.is_null() { None } else { Some(UnownedDriver { inner: driver }) }
579    }
580
581    /// Registers a callback which is triggered whenever the stall scanner should run.
582    pub fn register_stall_scanner(&self, scanner: StallScanner) {
583        unsafe {
584            fdf_env_register_stall_scanner(scanner.into_ptr());
585        }
586    }
587}
588
589/// A registration handle returned by [`Driver::register_resume_requester`].
590/// The user MUST call `unregister` to unregister the resume requester when it is no longer valid.
591#[derive(Debug)]
592pub struct ResumeRequesterRegistration {
593    driver_ptr: *const ffi::c_void,
594    requester_ptr: *mut fdf_env_resume_requester_t,
595}
596
597// SAFETY: The runtime API that we call in this object (fdf_env_register_resume_requester)
598// is thread-safe and can be called from any thread. We are also the exclusive maintainer of
599// requester_ptr's lifetime.
600unsafe impl Send for ResumeRequesterRegistration {}
601
602impl ResumeRequesterRegistration {
603    /// Unregisters the resume requester from the runtime and frees the memory associated with it.
604    pub fn unregister(mut self) {
605        // Unregister the callback from the runtime.
606        // SAFETY: The null pointer is handled correctly by the runtime. If driver_ptr is no longer valid
607        // in the driver runtime, it will be treated as a no-op.
608        unsafe {
609            fdf_sys::fdf_env_register_resume_requester(self.driver_ptr, null_mut());
610        }
611
612        // Reconstitute the box and free it.
613        // SAFETY: requester_ptr was created using Box::leak(Box::new(self)).
614        // This is the only location that we re-create the Box, with exclusive ownership of self.
615        let requester = unsafe { Box::from_raw(self.requester_ptr as *mut ResumeRequester) };
616        drop(requester);
617
618        self.requester_ptr = null_mut();
619    }
620}