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