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