Skip to main content

fdf_core/
dispatcher.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//! Safe bindings for the driver runtime dispatcher stable ABI
6
7use fdf_sys::*;
8use libasync_dispatcher::{
9    AsAsyncDispatcherRef, AsyncDispatcher, AsyncDispatcherRef, GetAsyncDispatcher, JoinHandle,
10    OnDispatcher, Task,
11};
12
13use core::cell::RefCell;
14use core::ffi::{self, CStr};
15use core::marker::PhantomData;
16use core::mem::ManuallyDrop;
17use core::ptr::{NonNull, null_mut};
18
19use zx::Status;
20
21use crate::shutdown_observer::ShutdownObserver;
22
23pub use fdf_sys::fdf_dispatcher_t;
24
25/// A marker trait for a function type that can be used as a shutdown observer for [`Dispatcher`].
26pub trait ShutdownObserverFn: FnOnce(DriverDispatcherRef<'_>) + Send + 'static {}
27impl<T> ShutdownObserverFn for T where T: FnOnce(DriverDispatcherRef<'_>) + Send + 'static {}
28
29/// A builder for [`Dispatcher`]s
30#[derive(Default)]
31pub struct DispatcherBuilder {
32    #[doc(hidden)]
33    pub options: u32,
34    #[doc(hidden)]
35    pub name: String,
36    #[doc(hidden)]
37    pub scheduler_role: String,
38    #[doc(hidden)]
39    pub shutdown_observer: Option<Box<dyn ShutdownObserverFn>>,
40}
41
42impl DispatcherBuilder {
43    /// See `FDF_DISPATCHER_OPTION_UNSYNCHRONIZED` in the C API
44    pub(crate) const UNSYNCHRONIZED: u32 = fdf_sys::FDF_DISPATCHER_OPTION_UNSYNCHRONIZED;
45    /// See `FDF_DISPATCHER_OPTION_ALLOW_SYNC_CALLS` in the C API
46    pub(crate) const ALLOW_THREAD_BLOCKING: u32 = fdf_sys::FDF_DISPATCHER_OPTION_ALLOW_SYNC_CALLS;
47    /// See `FDF_DISPATCHER_OPTION_NO_THREAD_MIGRATION` in the C API
48    pub(crate) const NO_THREAD_MIGRATION: u32 = fdf_sys::FDF_DISPATCHER_OPTION_NO_THREAD_MIGRATION;
49
50    /// Creates a new [`DispatcherBuilder`] that can be used to configure a new dispatcher.
51    /// For more information on the threading-related flags for the dispatcher, see
52    /// https://fuchsia.dev/fuchsia-src/concepts/drivers/driver-dispatcher-and-threads
53    pub fn new() -> Self {
54        Self::default()
55    }
56
57    /// Sets whether parallel callbacks in the callbacks set in the dispatcher are allowed. May
58    /// not be set with [`Self::allow_thread_blocking`].
59    ///
60    /// See https://fuchsia.dev/fuchsia-src/concepts/drivers/driver-dispatcher-and-threads
61    /// for more information on the threading model of driver dispatchers.
62    pub fn unsynchronized(mut self) -> Self {
63        assert!(
64            !self.allows_thread_blocking(),
65            "you may not create an unsynchronized dispatcher that allows synchronous calls"
66        );
67        self.options |= Self::UNSYNCHRONIZED;
68        self
69    }
70
71    /// Whether or not this is an unsynchronized dispatcher
72    pub fn is_unsynchronized(&self) -> bool {
73        (self.options & Self::UNSYNCHRONIZED) == Self::UNSYNCHRONIZED
74    }
75
76    /// This dispatcher may not share zircon threads with other drivers. May not be set with
77    /// [`Self::unsynchronized`].
78    ///
79    /// See https://fuchsia.dev/fuchsia-src/concepts/drivers/driver-dispatcher-and-threads
80    /// for more information on the threading model of driver dispatchers.
81    pub fn allow_thread_blocking(mut self) -> Self {
82        assert!(
83            !self.is_unsynchronized(),
84            "you may not create an unsynchronized dispatcher that allows synchronous calls"
85        );
86        self.options |= Self::ALLOW_THREAD_BLOCKING;
87        self
88    }
89
90    /// Whether or not this dispatcher allows synchronous calls
91    pub fn allows_thread_blocking(&self) -> bool {
92        (self.options & Self::ALLOW_THREAD_BLOCKING) == Self::ALLOW_THREAD_BLOCKING
93    }
94
95    /// This dispatcher may not run on more than one thread. This can only be set if the
96    /// dispatcher is being run on a scheduler role that does not allow sync calls on
97    /// any of its dispatchers.
98    ///
99    /// See https://fuchsia.dev/fuchsia-src/concepts/drivers/driver-dispatcher-and-threads
100    /// for more information on the threading model of driver dispatchers.
101    pub fn no_thread_migration(mut self) -> Self {
102        self.options |= Self::NO_THREAD_MIGRATION;
103        self
104    }
105
106    /// Whether or not this dispatcher is allowed to run on multiple threads
107    pub fn allows_thread_migration(&self) -> bool {
108        (self.options & Self::NO_THREAD_MIGRATION) == 0
109    }
110
111    /// A descriptive name for this dispatcher that is used in debug output and process
112    /// lists.
113    pub fn name(mut self, name: &str) -> Self {
114        self.name = name.to_string();
115        self
116    }
117
118    /// A hint string for the runtime that may or may not impact the priority the work scheduled
119    /// by this dispatcher is handled at. It may or may not impact the ability for other drivers
120    /// to share zircon threads with the dispatcher.
121    pub fn scheduler_role(mut self, role: &str) -> Self {
122        self.scheduler_role = role.to_string();
123        self
124    }
125
126    /// A callback to be called before after the dispatcher has completed asynchronous shutdown.
127    pub fn shutdown_observer<F: ShutdownObserverFn>(mut self, shutdown_observer: F) -> Self {
128        self.shutdown_observer = Some(Box::new(shutdown_observer));
129        self
130    }
131
132    /// Create the dispatcher as configured by this object. This must be called from a
133    /// thread managed by the driver runtime. The dispatcher returned is owned by the caller,
134    /// and will initiate asynchronous shutdown when the object is dropped unless
135    /// [`Dispatcher::release`] is called on it to convert it into an unowned [`DispatcherRef`].
136    pub fn create(self) -> Result<Dispatcher, Status> {
137        let mut out_dispatcher = null_mut();
138        let options = self.options;
139        let name = self.name.as_ptr() as *mut ffi::c_char;
140        let name_len = self.name.len();
141        let scheduler_role = self.scheduler_role.as_ptr() as *mut ffi::c_char;
142        let scheduler_role_len = self.scheduler_role.len();
143        let observer =
144            ShutdownObserver::new(self.shutdown_observer.unwrap_or_else(|| Box::new(|_| {})))
145                .into_ptr();
146        // SAFETY: all arguments point to memory that will be available for the duration
147        // of the call, except `observer`, which will be available until it is unallocated
148        // by the dispatcher exit handler.
149        Status::ok(unsafe {
150            fdf_dispatcher_create(
151                options,
152                name,
153                name_len,
154                scheduler_role,
155                scheduler_role_len,
156                observer,
157                &mut out_dispatcher,
158            )
159        })?;
160        // SAFETY: `out_dispatcher` is valid by construction if `fdf_dispatcher_create` returns
161        // ZX_OK.
162        Ok(Dispatcher(unsafe { NonNull::new_unchecked(out_dispatcher) }))
163    }
164
165    /// As with [`Self::create`], this creates a new dispatcher as configured by this object, but
166    /// instead of returning an owned reference it immediately releases the reference to be
167    /// managed by the driver runtime.
168    pub fn create_released(self) -> Result<AutoReleaseDispatcher, Status> {
169        self.create().map(Dispatcher::release)
170    }
171}
172
173/// An owned handle for a dispatcher managed by the driver runtime.
174#[derive(Debug)]
175pub struct Dispatcher(pub(crate) NonNull<fdf_dispatcher_t>);
176
177// SAFETY: The api of fdf_dispatcher_t is thread safe.
178unsafe impl Send for Dispatcher {}
179unsafe impl Sync for Dispatcher {}
180thread_local! {
181    pub(crate) static OVERRIDE_DISPATCHER: RefCell<Option<NonNull<fdf_dispatcher_t>>> = const { RefCell::new(None) };
182}
183
184impl Dispatcher {
185    /// Creates a dispatcher ref from a raw handle.
186    ///
187    /// # Safety
188    ///
189    /// Caller is responsible for ensuring that the given handle is valid and
190    /// not owned by any other wrapper that will free it at an arbitrary
191    /// time.
192    pub unsafe fn from_raw(handle: NonNull<fdf_dispatcher_t>) -> Self {
193        Self(handle)
194    }
195
196    fn get_raw_flags(&self) -> u32 {
197        // SAFETY: the inner fdf_dispatcher_t is valid by construction
198        unsafe { fdf_dispatcher_get_options(self.0.as_ptr()) }
199    }
200
201    /// Whether this dispatcher's tasks and futures can run on multiple threads at the same time.
202    pub fn is_unsynchronized(&self) -> bool {
203        (self.get_raw_flags() & DispatcherBuilder::UNSYNCHRONIZED) != 0
204    }
205
206    /// Whether this dispatcher is allowed to call blocking functions or not
207    pub fn allows_thread_blocking(&self) -> bool {
208        (self.get_raw_flags() & DispatcherBuilder::ALLOW_THREAD_BLOCKING) != 0
209    }
210
211    /// Whether this dispatcher is allowed to migrate threads, in which case it can't
212    /// be used for non-[`Send`] tasks.
213    pub fn allows_thread_migration(&self) -> bool {
214        (self.get_raw_flags() & DispatcherBuilder::NO_THREAD_MIGRATION) == 0
215    }
216
217    /// The scheduler role this dispatcher was created with, or an empty string if none was
218    /// specified.
219    pub fn scheduler_role(&self) -> String {
220        // SAFETY: the inner fdf_dispatcher_t is valid by construction, and the runtime returns a
221        // NUL-terminated string that lives as long as the dispatcher, which outlives this call.
222        let role = unsafe { CStr::from_ptr(fdf_dispatcher_get_scheduler_role(self.0.as_ptr())) };
223        role.to_string_lossy().into_owned()
224    }
225
226    /// Whether this is the dispatcher the current thread is running on
227    pub fn is_current_dispatcher(&self) -> bool {
228        // SAFETY: we don't do anything with the dispatcher pointer, and NULL is returned if this
229        // isn't a dispatcher-managed thread.
230        let current = unsafe { fdf_dispatcher_get_current_dispatcher() };
231        !current.is_null()
232            && (self.0.as_ptr() == current
233                || self.0.as_ptr() == unsafe { fdf_dispatcher_get_always_on_dispatcher(current) })
234    }
235
236    /// Releases ownership over this dispatcher and returns a [`DispatcherRef`]
237    /// that can be used to access it. The lifetime of this reference is static because it will
238    /// exist so long as this current driver is loaded, but the driver runtime will shut it down
239    /// when the driver is unloaded.
240    pub fn release(self) -> AutoReleaseDispatcher {
241        AutoReleaseDispatcher { dispatcher: ManuallyDrop::new(self) }
242    }
243
244    /// Returns a [`DispatcherRef`] that references this dispatcher with a lifetime constrained by
245    /// `self`.
246    pub fn as_dispatcher_ref(&self) -> DriverDispatcherRef<'_> {
247        DriverDispatcherRef(ManuallyDrop::new(Dispatcher(self.0)), PhantomData)
248    }
249}
250
251impl AsAsyncDispatcherRef for Dispatcher {
252    fn as_async_dispatcher_ref(&self) -> AsyncDispatcherRef<'_> {
253        let async_dispatcher =
254            NonNull::new(unsafe { fdf_dispatcher_get_async_dispatcher(self.0.as_ptr()) })
255                .expect("No async dispatcher on driver dispatcher");
256        unsafe { AsyncDispatcherRef::from_raw(async_dispatcher) }
257    }
258}
259
260impl Drop for Dispatcher {
261    fn drop(&mut self) {
262        // SAFETY: we only ever provide an owned `Dispatcher` to one owner, so when
263        // that one is dropped we can invoke the shutdown of the dispatcher
264        unsafe { fdf_dispatcher_shutdown_async(self.0.as_mut()) }
265    }
266}
267
268/// An owned reference to a driver runtime dispatcher that auto-releases when dropped. This gives
269/// you the best of both worlds of having an `Arc<Dispatcher>` and a `DispatcherRef<'static>`
270/// created by [`Dispatcher::release`]:
271///
272/// - You can vend [`Weak`]-like pointers to it that will not cause memory access errors if used
273///   after the dispatcher has shut down, like an [`Arc`].
274/// - You can tie its terminal lifetime to that of the driver itself.
275///
276/// This is particularly useful in tests.
277#[derive(Debug)]
278pub struct AutoReleaseDispatcher {
279    dispatcher: ManuallyDrop<Dispatcher>,
280}
281
282impl AutoReleaseDispatcher {
283    /// Creates a dispatcher ref from a raw handle.
284    ///
285    /// # Safety
286    ///
287    /// Caller is responsible for ensuring that the given handle is valid and
288    /// not owned by any other wrapper that will free it at an arbitrary
289    /// time.
290    pub unsafe fn from_raw(dispatcher: NonNull<fdf_dispatcher_t>) -> Self {
291        let dispatcher = ManuallyDrop::new(Dispatcher(dispatcher));
292        Self { dispatcher }
293    }
294
295    /// Returns a weakened reference to this dispatcher. This weak reference will only be valid so
296    /// long as the dispatcher is shutting down, after which it will no longer be usable to spawn
297    /// tasks on.
298    pub fn as_async_dispatcher(&self) -> AsyncDispatcher {
299        AsyncDispatcher::new(self)
300    }
301
302    /// Returns a [`DispatcherRef`] that references this dispatcher with a lifetime constrained by
303    /// `self`.
304    pub fn as_dispatcher_ref(&self) -> DriverDispatcherRef<'_> {
305        DriverDispatcherRef(ManuallyDrop::new(Dispatcher(self.dispatcher.0)), PhantomData)
306    }
307
308    /// Returns the Always-On interface of this dispatcher.
309    pub fn always_on_dispatcher(&self) -> AutoReleaseDispatcher {
310        // SAFETY: `self.dispatcher.0` is a valid, active `fdf_dispatcher_t` pointer owned by this
311        // `AutoReleaseDispatcher`.
312        let dispatcher_ref = unsafe { DriverDispatcherRef::from_raw(self.dispatcher.0) };
313        // SAFETY: The always-on dispatcher pointer returned by the runtime is guaranteed to remain
314        // valid for at least as long as the parent dispatcher is alive. Since this is an
315        // `AutoReleaseDispatcher`, the underlying dispatcher will not be shut down when dropped,
316        // and we wrap the new dispatcher in `ManuallyDrop` to ensure the same.
317        let dispatcher = unsafe { Dispatcher::from_raw(dispatcher_ref.always_on_dispatcher().0.0) };
318        Self { dispatcher: ManuallyDrop::new(dispatcher) }
319    }
320}
321
322impl AsAsyncDispatcherRef for AutoReleaseDispatcher {
323    fn as_async_dispatcher_ref(&self) -> AsyncDispatcherRef<'_> {
324        self.dispatcher.as_async_dispatcher_ref()
325    }
326}
327
328impl From<Dispatcher> for AutoReleaseDispatcher {
329    fn from(dispatcher: Dispatcher) -> Self {
330        Self { dispatcher: ManuallyDrop::new(dispatcher) }
331    }
332}
333
334/// An unowned reference to a driver runtime dispatcher such as is produced by calling
335/// [`Dispatcher::release`]. When this object goes out of scope it won't shut down the dispatcher,
336/// leaving that up to the driver runtime or another owner.
337#[derive(Debug)]
338pub struct DriverDispatcherRef<'a>(ManuallyDrop<Dispatcher>, PhantomData<&'a Dispatcher>);
339
340impl<'a> DriverDispatcherRef<'a> {
341    /// Creates a dispatcher ref from a raw handle.
342    ///
343    /// # Safety
344    ///
345    /// Caller is responsible for ensuring that the given handle is valid for
346    /// the lifetime `'a`.
347    pub unsafe fn from_raw(handle: NonNull<fdf_dispatcher_t>) -> Self {
348        // SAFETY: Caller promises the handle is valid.
349        Self(ManuallyDrop::new(unsafe { Dispatcher::from_raw(handle) }), PhantomData)
350    }
351
352    /// Creates a dispatcher ref from an [`AsyncDispatcherRef`].
353    ///
354    /// # Panics
355    ///
356    /// Note that this will cause an assert if the [`AsyncDispatcherRef`] was not created from a
357    /// driver dispatcher in the first place.
358    pub fn from_async_dispatcher(dispatcher: AsyncDispatcherRef<'a>) -> Self {
359        let handle = NonNull::new(unsafe {
360            fdf_dispatcher_downcast_async_dispatcher(dispatcher.inner().as_ptr())
361        })
362        .unwrap();
363        unsafe { Self::from_raw(handle) }
364    }
365
366    /// Gets the raw handle from this dispatcher ref.
367    ///
368    /// # Safety
369    ///
370    /// Caller is responsible for ensuring that the dispatcher handle is used safely.
371    pub unsafe fn as_raw(&mut self) -> *mut fdf_dispatcher_t {
372        unsafe { self.0.0.as_mut() }
373    }
374
375    /// Returns a [`DispatcherRef`] for the always-on dispatcher associated with this dispatcher,
376    /// preserving the lifetime parameter of the parent dispatcher.
377    pub fn always_on_dispatcher(&self) -> DriverDispatcherRef<'a> {
378        // SAFETY: The pointer being passed in is valid as its coming from a DispatcherRef.
379        let ptr = unsafe { fdf_dispatcher_get_always_on_dispatcher(self.0.0.as_ptr()) };
380        DriverDispatcherRef(
381            ManuallyDrop::new(Dispatcher(NonNull::new(ptr).expect("Always-on dispatcher is NULL"))),
382            PhantomData,
383        )
384    }
385
386    /// Registers a wake vector event with the dispatcher.
387    ///
388    /// The wake vector stays registered until the returned [`WakeVectorRegistration`] is dropped
389    /// or [`WakeVectorRegistration::unregister`] is called.
390    pub fn register_wake_vector(
391        &self,
392        handle: &impl zx::AsHandleRef,
393        signals: zx::Signals,
394    ) -> Result<WakeVectorRegistration, Status> {
395        let raw_handle = handle.as_handle_ref().raw_handle();
396        // SAFETY: `self.0.0` is a valid `fdf_dispatcher_t` pointer, and `raw_handle` is
397        // borrowed per the C API.
398        Status::ok(unsafe {
399            fdf_sys::fdf_dispatcher_register_wake_vector(
400                self.0.0.as_ptr(),
401                raw_handle,
402                signals.bits(),
403            )
404        })?;
405        Ok(WakeVectorRegistration {
406            dispatcher: Some(AsyncDispatcher::new(self)),
407            handle: raw_handle,
408            signals,
409        })
410    }
411}
412
413/// A registration handle for a wake vector event, returned by
414/// [`DriverDispatcherRef::register_wake_vector`] and
415/// [`OnDriverDispatcher::register_wake_vector`]. Caller must hold on to this as long as they want
416/// this wake vector to be active.
417///
418/// Automatically unregisters the wake vector from the dispatcher when dropped.
419#[derive(Debug)]
420pub struct WakeVectorRegistration {
421    /// Always `Some` until the registration is unregistered.
422    dispatcher: Option<AsyncDispatcher>,
423    handle: zx::sys::zx_handle_t,
424    signals: zx::Signals,
425}
426
427impl WakeVectorRegistration {
428    /// Unregisters the wake vector. Unlike dropping the registration, this reports failures.
429    ///
430    /// Returns [`Status::NOT_FOUND`] if the dispatcher no longer has a wake vector registered for
431    /// this handle. That can happen if another registration for the same handle was unregistered
432    /// with empty signals, which removes the handle's wake vector entirely.
433    pub fn unregister(mut self) -> Result<(), Status> {
434        self.unregister_inner()
435    }
436
437    fn unregister_inner(&mut self) -> Result<(), Status> {
438        let Some(dispatcher) = self.dispatcher.take() else { return Ok(()) };
439        let dispatcher_ref =
440            DriverDispatcherRef::from_async_dispatcher(dispatcher.as_async_dispatcher_ref());
441        // SAFETY: `dispatcher_ref.0.0` is a valid `fdf_dispatcher_t` pointer, and `self.handle`
442        // is borrowed per the C API.
443        Status::ok(unsafe {
444            fdf_sys::fdf_dispatcher_unregister_wake_vector(
445                dispatcher_ref.0.0.as_ptr(),
446                self.handle,
447                self.signals.bits(),
448            )
449        })
450    }
451}
452
453impl Drop for WakeVectorRegistration {
454    fn drop(&mut self) {
455        // The only possible error is NOT_FOUND (see `unregister`), which means the wake vector
456        // is already gone, so there is nothing left to clean up.
457        let _: Result<(), Status> = self.unregister_inner();
458    }
459}
460
461/// Used to wrap a non-send future as send when we've dynamically checked that the dispatcher
462/// we're going to spawn it on is non-[`Send`]-safe.
463///
464/// This should only ever be used after validating that the dispatcher is the currently running
465/// one and that the dispatcher does not migrate threads.
466///
467/// This is an internal implementation detail and should never be made public.
468struct AddSendFuture<T>(T);
469
470impl<T: Future> Future for AddSendFuture<T> {
471    type Output = T::Output;
472
473    fn poll(
474        self: std::pin::Pin<&mut Self>,
475        cx: &mut std::task::Context<'_>,
476    ) -> std::task::Poll<Self::Output> {
477        // SAFETY: self.0 is pinned if self is.
478        let fut = unsafe { self.map_unchecked_mut(|fut| &mut fut.0) };
479        fut.poll(cx)
480    }
481}
482
483// SAFETY: We are forcing this future to be [`Send`] even though the inner future is not because
484// we validate at runtime before spawning the task that the dispatcher is correctly configured to
485// do the right thing with it.
486unsafe impl<T> Send for AddSendFuture<T> {}
487
488/// Makes available additional functionality available on driver dispatchers on top of what's
489/// available on [`OnDispatcher`].
490pub trait OnDriverDispatcher: OnDispatcher {
491    /// Spawn an asynchronous local task on this dispatcher. If this returns [`Ok`] then the task
492    /// has successfully been scheduled and will run or be cancelled and dropped when the dispatcher
493    /// shuts down. The returned future's result will be [`Ok`] if the future completed
494    /// successfully, or an [`Err`] if the task did not complete for some reason (like the
495    /// dispatcher shut down).
496    ///
497    /// Unlike [`OnDispatcher::spawn`], this will accept a future that does not implement [`Send`]. If
498    /// called from a thread other than the one the dispatcher is running on or the dispatcher
499    /// is not guaranteed to always poll from the same thread, this will return
500    /// [`Status::BAD_STATE`].
501    ///
502    /// Returns a [`JoinHandle`] that will detach the future when dropped.
503    fn spawn_local(&self, future: impl Future<Output = ()> + 'static) -> JoinHandle<()>
504    where
505        Self: 'static,
506    {
507        self.compute_local(future).detach_on_drop()
508    }
509
510    /// Spawn a local asynchronous task that outputs type 'T' on this dispatcher. The returned future's
511    /// result will be [`Ok`] if the task was started and completed successfully, or an [`Err`] if
512    /// the task couldn't be started or failed to complete (for example because the dispatcher was
513    /// shutting down).
514    ///
515    /// Returns a [`Task`] that will cancel the future when dropped.
516    ///
517    /// Unlike [`OnDispatcher::compute`], this will accept a future that does not implement [`Send`]. If
518    /// called from a thread other than the one the dispatcher is running on or the dispatcher
519    /// is not guaranteed to always poll from the same thread, this will return
520    /// [`Status::BAD_STATE`].
521    ///
522    /// TODO(470088116): This may be the cause of some flakes, so care should be used with it
523    /// in critical paths for now.
524    fn compute_local<T: Send + 'static>(&self, future: impl Future<Output = T> + 'static) -> Task<T>
525    where
526        Self: 'static,
527    {
528        let Some(dispatcher) = self.try_get_async_dispatcher() else {
529            return Task::new_failed(Status::BAD_STATE);
530        };
531        let dispatcher =
532            DriverDispatcherRef::from_async_dispatcher(dispatcher.as_async_dispatcher_ref());
533        if dispatcher.0.is_current_dispatcher() && !dispatcher.0.allows_thread_migration() {
534            OnDispatcher::compute(self, AddSendFuture(future))
535        } else {
536            Task::new_failed(Status::BAD_STATE)
537        }
538    }
539
540    /// Returns the Always-On interface of this driver dispatcher.
541    fn always_on_dispatcher(&self) -> AsyncDispatcher {
542        let dispatcher = self.get_async_dispatcher();
543        let dispatcher_ref =
544            DriverDispatcherRef::from_async_dispatcher(dispatcher.as_async_dispatcher_ref());
545        AsyncDispatcher::new(&dispatcher_ref.always_on_dispatcher())
546    }
547
548    /// Registers a wake vector event with this driver dispatcher.
549    fn register_wake_vector(
550        &self,
551        handle: &impl zx::AsHandleRef,
552        signals: zx::Signals,
553    ) -> Result<WakeVectorRegistration, Status> {
554        let dispatcher = self.try_get_async_dispatcher().ok_or(Status::BAD_STATE)?;
555        let dispatcher_ref =
556            DriverDispatcherRef::from_async_dispatcher(dispatcher.as_async_dispatcher_ref());
557        dispatcher_ref.register_wake_vector(handle, signals)
558    }
559}
560
561impl<'a> AsAsyncDispatcherRef for DriverDispatcherRef<'a> {
562    fn as_async_dispatcher_ref(&self) -> AsyncDispatcherRef<'_> {
563        self.0.as_async_dispatcher_ref()
564    }
565}
566
567impl<'a> Clone for DriverDispatcherRef<'a> {
568    fn clone(&self) -> Self {
569        Self(ManuallyDrop::new(Dispatcher(self.0.0)), PhantomData)
570    }
571}
572
573impl<'a> core::ops::Deref for DriverDispatcherRef<'a> {
574    type Target = Dispatcher;
575    fn deref(&self) -> &Self::Target {
576        &self.0
577    }
578}
579
580impl<'a> core::ops::DerefMut for DriverDispatcherRef<'a> {
581    fn deref_mut(&mut self) -> &mut Self::Target {
582        &mut self.0
583    }
584}
585
586/// Note: This may panic (or assert in C++) if its methods are run on a dispatcher that is not
587/// a driver dispatcher.
588impl<T> OnDriverDispatcher for T where T: AsAsyncDispatcherRef + Clone {}
589
590/// A placeholder for the currently active dispatcher.
591#[derive(Clone, Copy, Debug, Default, PartialEq)]
592pub struct CurrentDispatcher;
593
594impl GetAsyncDispatcher for CurrentDispatcher {
595    fn try_get_async_dispatcher(&self) -> Option<AsyncDispatcher> {
596        OVERRIDE_DISPATCHER
597            .with(|global| *global.borrow())
598            .or_else(|| {
599                // SAFETY: NonNull::new will null-check that we have a current dispatcher.
600                NonNull::new(unsafe { fdf_dispatcher_get_current_dispatcher() })
601            })
602            .map(|dispatcher| {
603                // SAFETY: We constrain the lifetime of the `DispatcherRef` we provide to the
604                // function below to the span of the current function. Since we are running on
605                // the dispatcher, or another dispatcher that is bound to the same lifetime (through
606                // override_dispatcher), we can be sure that the dispatcher will not be shut
607                // down before that function completes.
608                let async_dispatcher = NonNull::new(unsafe {
609                    fdf_dispatcher_get_async_dispatcher(dispatcher.as_ptr())
610                })
611                .expect("No async dispatcher on driver dispatcher");
612                AsyncDispatcher::new(&unsafe { AsyncDispatcherRef::from_raw(async_dispatcher) })
613            })
614    }
615}
616
617impl OnDriverDispatcher for CurrentDispatcher {}
618
619#[cfg(test)]
620mod tests {
621    use super::*;
622
623    use std::sync::{Once, mpsc};
624
625    use futures::channel::mpsc as async_mpsc;
626    use futures::{SinkExt, StreamExt};
627    use zx::sys::ZX_OK;
628
629    use core::ffi::{c_char, c_void};
630    use core::ptr::null_mut;
631
632    static GLOBAL_DRIVER_ENV: Once = Once::new();
633    const NO_SYNC_CALLS_ROLE: &str = "no sync calls role";
634
635    pub fn ensure_driver_env() {
636        GLOBAL_DRIVER_ENV.call_once(|| {
637            // SAFETY: calling fdf_env_start, which does not have any soundness
638            // concerns for rust code, and this is only used in tests.
639            unsafe {
640                assert_eq!(fdf_env_start(0), ZX_OK);
641                assert_eq!(
642                    fdf_env_set_scheduler_role_opts(
643                        NO_SYNC_CALLS_ROLE.as_ptr() as *const c_char,
644                        NO_SYNC_CALLS_ROLE.len(),
645                        FDF_SCHEDULER_ROLE_OPTION_NO_SYNC_CALLS
646                    ),
647                    ZX_OK
648                );
649            }
650        });
651    }
652    pub fn with_raw_dispatcher<T>(name: &str, p: impl FnOnce(AsyncDispatcher) -> T) -> T {
653        with_raw_dispatcher_flags(name, DispatcherBuilder::ALLOW_THREAD_BLOCKING, "", p)
654    }
655
656    pub(crate) fn with_raw_dispatcher_flags<T>(
657        name: &str,
658        flags: u32,
659        scheduler_role: &str,
660        p: impl FnOnce(AsyncDispatcher) -> T,
661    ) -> T {
662        ensure_driver_env();
663
664        let (shutdown_tx, shutdown_rx) = mpsc::channel();
665        let mut dispatcher = null_mut();
666        let mut observer = ShutdownObserver::new(move |dispatcher| {
667            // SAFETY: we verify that the dispatcher has no tasks left queued in it,
668            // just because this is testing code.
669            assert!(!unsafe { fdf_env_dispatcher_has_queued_tasks(dispatcher.0.0.as_ptr()) });
670            shutdown_tx.send(()).unwrap();
671        })
672        .into_ptr();
673        let driver_ptr = &mut observer as *mut _ as *mut c_void;
674        // SAFETY: The pointers we pass to this function are all stable for the
675        // duration of this function, and are not available to copy or clone to
676        // client code (only through a ref to the non-`Clone`` `Dispatcher`
677        // wrapper).
678        let res = unsafe {
679            fdf_env_dispatcher_create_with_owner(
680                driver_ptr,
681                flags,
682                name.as_ptr() as *const c_char,
683                name.len(),
684                scheduler_role.as_ptr() as *const c_char,
685                scheduler_role.len(),
686                observer,
687                &mut dispatcher,
688            )
689        };
690        assert_eq!(res, ZX_OK);
691        let dispatcher = Dispatcher(NonNull::new(dispatcher).unwrap());
692
693        let res = p(AsyncDispatcher::new(&dispatcher));
694
695        drop(dispatcher);
696        shutdown_rx.recv().unwrap();
697
698        res
699    }
700
701    #[test]
702    fn start_test_dispatcher() {
703        with_raw_dispatcher("testing", |dispatcher| {
704            println!("hello {dispatcher:?}");
705        })
706    }
707
708    #[test]
709    fn scheduler_role() {
710        // Non-empty roles are covered by the runtime's own `DispatcherTest.GetSchedulerRole`;
711        // creating a dispatcher with a role here hits b/488397193.
712        with_raw_dispatcher("no role", |dispatcher| {
713            let dispatcher =
714                DriverDispatcherRef::from_async_dispatcher(dispatcher.as_async_dispatcher_ref());
715            assert_eq!(dispatcher.scheduler_role(), "");
716            assert_eq!(dispatcher.always_on_dispatcher().scheduler_role(), "");
717        });
718    }
719
720    #[test]
721    fn post_task_on_dispatcher() {
722        with_raw_dispatcher("testing task", |dispatcher| {
723            let (tx, rx) = mpsc::channel();
724            dispatcher
725                .post_task_sync(move |status| {
726                    assert_eq!(status, Ok(()));
727                    tx.send(status).unwrap();
728                })
729                .unwrap();
730            assert_eq!(rx.recv().unwrap(), Ok(()));
731        });
732    }
733
734    #[test]
735    fn post_task_on_subdispatcher() {
736        let (shutdown_tx, shutdown_rx) = mpsc::channel();
737        with_raw_dispatcher("testing task top level", move |dispatcher| {
738            let (tx, rx) = mpsc::channel();
739            let (inner_tx, inner_rx) = mpsc::channel();
740            dispatcher
741                .post_task_sync(move |status| {
742                    assert_eq!(status, Ok(()));
743                    let inner = DispatcherBuilder::new()
744                        .name("testing task second level")
745                        .scheduler_role("")
746                        .allow_thread_blocking()
747                        .shutdown_observer(move |_dispatcher| {
748                            println!("shutdown observer called");
749                            shutdown_tx.send(1).unwrap();
750                        })
751                        .create()
752                        .unwrap();
753                    inner
754                        .post_task_sync(move |status| {
755                            assert_eq!(status, Ok(()));
756                            tx.send(status).unwrap();
757                        })
758                        .unwrap();
759                    // we want to make sure the inner dispatcher lives long
760                    // enough to run the task, so we sent it out to the outer
761                    // closure.
762                    inner_tx.send(inner).unwrap();
763                })
764                .unwrap();
765            assert_eq!(rx.recv().unwrap(), Ok(()));
766            inner_rx.recv().unwrap();
767        });
768        assert_eq!(shutdown_rx.recv().unwrap(), 1);
769    }
770
771    #[test]
772    fn spawn_local_fails_on_normal_dispatcher() {
773        let (shutdown_tx, shutdown_rx) = mpsc::channel();
774        with_raw_dispatcher("spawn local failures", move |dispatcher| {
775            let inside_dispatcher = dispatcher.clone();
776            dispatcher.spawn(async move {
777                assert_eq!(
778                    inside_dispatcher.spawn_local(futures::future::ready(())).await.unwrap_err(),
779                    Status::BAD_STATE
780                );
781                assert_eq!(
782                    inside_dispatcher.compute_local(futures::future::ready(())).await.unwrap_err(),
783                    Status::BAD_STATE
784                );
785                shutdown_tx.send(()).unwrap();
786            });
787            shutdown_rx.recv().unwrap();
788        });
789    }
790
791    #[test]
792    #[ignore = "Pending resolution of b/488397193"]
793    fn spawn_local_succeeds_on_no_thread_migration_dispatcher() {
794        let (tx, rx) = mpsc::channel();
795        with_raw_dispatcher_flags(
796            "spawn local success",
797            FDF_DISPATCHER_OPTION_NO_THREAD_MIGRATION,
798            NO_SYNC_CALLS_ROLE,
799            move |dispatcher| {
800                let inside_dispatcher = dispatcher.clone();
801                dispatcher.spawn(async move {
802                    let tx_clone = tx.clone();
803                    inside_dispatcher.spawn_local(async move {
804                        tx_clone.send(()).unwrap();
805                    });
806                    inside_dispatcher
807                        .compute_local(async move {
808                            tx.send(()).unwrap();
809                        })
810                        .await
811                        .unwrap();
812                });
813                // one empty object received each for spawn and compute _local.
814                rx.recv().unwrap();
815                rx.recv().unwrap();
816            },
817        );
818    }
819
820    #[test]
821    #[ignore = "Pending resolution of b/488397193"]
822    fn spawn_local_fails_on_no_thread_migration_dispatcher_from_different_thread() {
823        with_raw_dispatcher_flags(
824            "spawn local success",
825            FDF_DISPATCHER_OPTION_NO_THREAD_MIGRATION,
826            NO_SYNC_CALLS_ROLE,
827            move |dispatcher| {
828                let mut executor = fuchsia_async::LocalExecutor::default();
829                executor.run_singlethreaded(async {
830                    // we are not currently running in any driver dispatcher here, so this is a
831                    // context where the 'current dispatcher' is definitely not the one in question.
832                    assert_eq!(
833                        dispatcher.spawn_local(futures::future::ready(())).await.unwrap_err(),
834                        Status::BAD_STATE
835                    );
836                    assert_eq!(
837                        dispatcher.compute_local(futures::future::ready(())).await.unwrap_err(),
838                        Status::BAD_STATE
839                    );
840                });
841            },
842        );
843    }
844
845    async fn ping(mut tx: async_mpsc::Sender<u8>, mut rx: async_mpsc::Receiver<u8>) {
846        println!("starting ping!");
847        tx.send(0).await.unwrap();
848        while let Some(next) = rx.next().await {
849            println!("ping! {next}");
850            tx.send(next + 1).await.unwrap();
851        }
852    }
853
854    async fn pong(
855        fin_tx: std::sync::mpsc::Sender<()>,
856        mut tx: async_mpsc::Sender<u8>,
857        mut rx: async_mpsc::Receiver<u8>,
858    ) {
859        println!("starting pong!");
860        while let Some(next) = rx.next().await {
861            println!("pong! {next}");
862            if next > 10 {
863                println!("bye!");
864                break;
865            }
866            tx.send(next + 1).await.unwrap();
867        }
868        fin_tx.send(()).unwrap();
869    }
870
871    #[test]
872    fn async_ping_pong() {
873        with_raw_dispatcher("async ping pong", |dispatcher| {
874            let (fin_tx, fin_rx) = mpsc::channel();
875            let (ping_tx, pong_rx) = async_mpsc::channel(10);
876            let (pong_tx, ping_rx) = async_mpsc::channel(10);
877            dispatcher.spawn(ping(ping_tx, ping_rx));
878            dispatcher.spawn(pong(fin_tx, pong_tx, pong_rx));
879
880            fin_rx.recv().expect("to receive final value");
881        });
882    }
883
884    async fn slow_pong(
885        fin_tx: std::sync::mpsc::Sender<()>,
886        mut tx: async_mpsc::Sender<u8>,
887        mut rx: async_mpsc::Receiver<u8>,
888    ) {
889        use zx::MonotonicDuration;
890        println!("starting pong!");
891        while let Some(next) = rx.next().await {
892            println!("pong! {next}");
893            fuchsia_async::Timer::new(fuchsia_async::MonotonicInstant::after(
894                MonotonicDuration::from_seconds(1),
895            ))
896            .await;
897            if next > 10 {
898                println!("bye!");
899                break;
900            }
901            tx.send(next + 1).await.unwrap();
902        }
903        fin_tx.send(()).unwrap();
904    }
905
906    #[test]
907    fn mixed_executor_async_ping_pong() {
908        with_raw_dispatcher("async ping pong", |dispatcher| {
909            let (fin_tx, fin_rx) = mpsc::channel();
910            let (ping_tx, pong_rx) = async_mpsc::channel(10);
911            let (pong_tx, ping_rx) = async_mpsc::channel(10);
912
913            // spawn ping on the driver dispatcher
914            dispatcher.spawn(ping(ping_tx, ping_rx));
915
916            // and run pong on the fuchsia_async executor
917            let mut executor = fuchsia_async::LocalExecutor::default();
918            executor.run_singlethreaded(slow_pong(fin_tx, pong_tx, pong_rx));
919
920            fin_rx.recv().expect("to receive final value");
921        });
922    }
923
924    #[test]
925    fn wake_vector_registration() {
926        with_raw_dispatcher("wake vector test", |dispatcher| {
927            let dispatcher_ref =
928                DriverDispatcherRef::from_async_dispatcher(dispatcher.as_async_dispatcher_ref());
929            let event = zx::Event::create();
930            let signals = zx::Signals::USER_0;
931
932            // Register and explicitly unregister.
933            let reg = dispatcher_ref.register_wake_vector(&event, signals).unwrap();
934            assert_eq!(reg.unregister(), Ok(()));
935
936            // Unregistering with empty signals removes the handle's wake vector entirely, so a
937            // second registration for the same handle reports NOT_FOUND.
938            let reg_all =
939                dispatcher_ref.register_wake_vector(&event, zx::Signals::empty()).unwrap();
940            let reg_user0 = dispatcher_ref.register_wake_vector(&event, signals).unwrap();
941            assert_eq!(reg_all.unregister(), Ok(()));
942            assert_eq!(reg_user0.unregister(), Err(Status::NOT_FOUND));
943
944            // Test automatic unregister on drop via OnDriverDispatcher.
945            {
946                let _reg_drop = dispatcher.register_wake_vector(&event, signals).unwrap();
947            }
948        });
949    }
950
951    #[test]
952    fn always_on_dispatcher_runs_task_and_irq_while_suspended() {
953        ensure_driver_env();
954
955        let (shutdown_tx, shutdown_rx) = mpsc::channel();
956        let mut dispatcher = null_mut();
957        let mut observer = ShutdownObserver::new(move |dispatcher| {
958            assert!(!unsafe { fdf_env_dispatcher_has_queued_tasks(dispatcher.0.0.as_ptr()) });
959            shutdown_tx.send(()).unwrap();
960        })
961        .into_ptr();
962        let driver_ptr = &mut observer as *mut _ as *mut c_void;
963        let name = "always_on_test";
964        let res = unsafe {
965            fdf_env_dispatcher_create_with_owner(
966                driver_ptr,
967                DispatcherBuilder::ALLOW_THREAD_BLOCKING,
968                name.as_ptr() as *const c_char,
969                name.len(),
970                "".as_ptr() as *const c_char,
971                0,
972                observer,
973                &mut dispatcher,
974            )
975        };
976        assert_eq!(res, ZX_OK);
977        let dispatcher_owner = Dispatcher(NonNull::new(dispatcher).unwrap());
978        let dispatcher = AsyncDispatcher::new(&dispatcher_owner);
979        let always_on = dispatcher.always_on_dispatcher();
980
981        #[repr(C)]
982        struct SuspendCompleter {
983            base: fdf_env_suspend_completer_t,
984            tx: mpsc::Sender<()>,
985        }
986        unsafe extern "C" fn on_suspended(completer: *mut fdf_env_suspend_completer_t) {
987            let completer = unsafe { &*(completer as *const SuspendCompleter) };
988            completer.tx.send(()).unwrap();
989        }
990        let (suspend_tx, suspend_rx) = mpsc::channel();
991        let mut completer = SuspendCompleter {
992            base: fdf_env_suspend_completer_t { handler: Some(on_suspended) },
993            tx: suspend_tx,
994        };
995        unsafe {
996            fdf_env_driver_suspend(driver_ptr, &mut completer.base);
997        }
998        suspend_rx.recv().unwrap();
999
1000        let (normal_tx, normal_rx) = mpsc::channel();
1001        dispatcher.spawn(async move {
1002            normal_tx.send(()).unwrap();
1003        });
1004
1005        let (always_on_tx, always_on_rx) = mpsc::channel();
1006        let irq = zx::Interrupt::create_virtual().unwrap();
1007        let mut on_irq = libasync::OnInterrupt::new_on(
1008            always_on.clone(),
1009            irq.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap(),
1010        );
1011        always_on.spawn(async move {
1012            let _ = on_irq.next().await.unwrap().unwrap();
1013            always_on_tx.send(()).unwrap();
1014        });
1015        irq.trigger(zx::BootInstant::from_nanos(1234)).unwrap();
1016
1017        // Always-on task and interrupt run while the driver dispatcher is suspended.
1018        always_on_rx.recv().unwrap();
1019        // Normal task remains queued on sleep_queue_ while suspended.
1020        assert!(normal_rx.try_recv().is_err());
1021
1022        unsafe {
1023            fdf_env_driver_resume(driver_ptr);
1024        }
1025        normal_rx.recv().unwrap();
1026
1027        drop(dispatcher_owner);
1028        shutdown_rx.recv().unwrap();
1029    }
1030}