Skip to main content

input_pipeline_dso/
dispatcher.rs

1// Copyright 2026 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
5use core::task::Context;
6use fidl_next::ClientEnd;
7use futures::prelude::*;
8use futures::task::Poll;
9use pin_project_lite::pin_project;
10use std::pin::Pin;
11
12#[cfg(feature = "dso")]
13pub use dso::*;
14
15#[cfg(not(feature = "dso"))]
16pub use elf::*;
17
18pin_project! {
19    #[derive(Debug)]
20    #[must_use = "futures do nothing unless polled"]
21    pub struct OnTimeout<F, T, OT> {
22        #[pin]
23        timer: T,
24        #[pin]
25        future: F,
26        on_timeout: Option<OT>,
27    }
28}
29
30impl<F: Future, T, OT> Future for OnTimeout<F, T, OT>
31where
32    T: Future<Output = ()> + 'static,
33    OT: FnOnce() -> F::Output,
34{
35    type Output = F::Output;
36
37    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
38        let this = self.project();
39        if let Poll::Ready(item) = this.future.poll(cx) {
40            return Poll::Ready(item);
41        }
42        if let Poll::Ready(()) = this.timer.poll(cx) {
43            let ot = this.on_timeout.take().expect("polled with timeout after completion");
44            let item = (ot)();
45            return Poll::Ready(item);
46        }
47        Poll::Pending
48    }
49}
50
51/// A wrapper for a future which will complete with a provided closure when a timeout occurs. This
52/// is forked from [`fuchsia_async::OnTimeout`] because that has a fixed dependency on
53/// [`fuchsia_async::Timer`] which driver dispatcher does not support.
54pub trait TimeoutExt: Future + Sized {
55    fn on_timeout<T, OT>(self, timer: T, on_timeout: OT) -> OnTimeout<Self, T, OT>
56    where
57        T: Future<Output = ()> + 'static,
58        OT: FnOnce() -> Self::Output,
59    {
60        OnTimeout { timer, future: self, on_timeout: Some(on_timeout) }
61    }
62}
63
64impl<F: Future + Sized> TimeoutExt for F {}
65
66#[derive(Clone, Default)]
67pub struct Dispatcher {}
68
69mod dso {
70    #![cfg(feature = "dso")]
71
72    pub use super::*;
73    use fdf::{AsyncDispatcher, OnDriverDispatcher};
74    use libasync::DispatcherTimerExt;
75
76    #[derive(Debug, Clone, Copy, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
77    #[repr(transparent)]
78    pub struct MonotonicInstant(zx::MonotonicInstant);
79
80    impl From<zx::MonotonicInstant> for MonotonicInstant {
81        fn from(o: zx::MonotonicInstant) -> Self {
82            Self(o)
83        }
84    }
85
86    impl From<MonotonicInstant> for zx::MonotonicInstant {
87        fn from(o: MonotonicInstant) -> Self {
88            o.0
89        }
90    }
91
92    impl MonotonicInstant {
93        pub fn now() -> Self {
94            Self(zx::MonotonicInstant::get())
95        }
96
97        pub fn into_nanos(&self) -> i64 {
98            self.0.into_nanos()
99        }
100
101        pub fn into_zx(self) -> zx::MonotonicInstant {
102            self.0
103        }
104
105        pub fn after(duration: zx::MonotonicDuration) -> Self {
106            Self(zx::MonotonicInstant::after(duration))
107        }
108    }
109
110    pub type Transport = libasync_fidl::AsyncChannel<Dispatcher>;
111    pub type DriverTransport = fdf_fidl::DriverChannel<fdf::CurrentDispatcher>;
112
113    #[derive(Debug)]
114    pub struct TaskHandle<T> {
115        handle: Option<::libasync::JoinHandle<T>>,
116        detached: bool,
117    }
118
119    impl<T> Drop for TaskHandle<T> {
120        fn drop(&mut self) {
121            if !self.detached {
122                self.handle.as_mut().take().map(|h| {
123                    _ = h.abort();
124                });
125            }
126        }
127    }
128
129    impl TaskHandle<()> {
130        pub fn detach(mut self) {
131            self.detached = true
132        }
133    }
134
135    impl<T: 'static> Future for TaskHandle<T> {
136        type Output = T;
137
138        fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
139            match self.handle.as_mut().unwrap().poll_unpin(cx) {
140                Poll::Pending => Poll::Pending,
141                Poll::Ready(Ok(t)) => Poll::Ready(t),
142                Poll::Ready(Err(e)) => panic!("TaskHandle: polled unexpected error {e:?}"),
143            }
144        }
145    }
146
147    impl Dispatcher {
148        #[must_use]
149        pub fn spawn_local(future: impl Future<Output = ()> + 'static) -> TaskHandle<()>
150        where
151            Self: 'static,
152        {
153            // This should never panic if the dispatcher is valid.
154            TaskHandle { handle: Some(fdf::CurrentDispatcher.spawn_local(future)), detached: false }
155        }
156
157        pub fn after_deadline(deadline: MonotonicInstant) -> impl Future<Output = ()> + 'static {
158            let f = fdf::CurrentDispatcher.after_deadline(deadline.into());
159            async move {
160                // This should never panic if the dispatcher is valid.
161                f.await.expect("Dispatcher::after_deadline");
162            }
163        }
164
165        pub fn client_from_zx_channel<P>(
166            client_end: ClientEnd<P, zx::Channel>,
167        ) -> ClientEnd<P, Transport> {
168            libasync_fidl::AsyncChannel::<Dispatcher>::client_from_zx_channel(client_end)
169        }
170    }
171
172    impl fdf::GetAsyncDispatcher for Dispatcher {
173        fn try_get_async_dispatcher(&self) -> Option<AsyncDispatcher> {
174            fdf::CurrentDispatcher.try_get_async_dispatcher()
175        }
176    }
177}
178
179mod elf {
180    #![cfg(not(feature = "dso"))]
181
182    pub use super::*;
183
184    pub type MonotonicInstant = fuchsia_async::MonotonicInstant;
185
186    pub type Transport = zx::Channel;
187
188    #[derive(Debug)]
189    pub struct TaskHandle<T>(fuchsia_async::Task<T>);
190
191    impl TaskHandle<()> {
192        pub fn detach(self) {
193            self.0.detach();
194        }
195    }
196
197    #[cfg(test)]
198    impl<T: 'static> From<fuchsia_async::Task<T>> for TaskHandle<T> {
199        fn from(task: fuchsia_async::Task<T>) -> Self {
200            Self(task)
201        }
202    }
203
204    impl<T: 'static> Future for TaskHandle<T> {
205        type Output = T;
206
207        fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
208            match self.0.poll_unpin(cx) {
209                Poll::Ready(t) => Poll::Ready(t),
210                Poll::Pending => Poll::Pending,
211            }
212        }
213    }
214
215    impl Dispatcher {
216        #[must_use]
217        pub fn spawn_local(future: impl Future<Output = ()> + 'static) -> TaskHandle<()>
218        where
219            Self: 'static,
220        {
221            TaskHandle(fuchsia_async::Task::local(future))
222        }
223
224        pub fn after_deadline(deadline: MonotonicInstant) -> impl Future<Output = ()> + 'static {
225            fuchsia_async::Timer::new(deadline)
226        }
227
228        pub fn client_from_zx_channel<P>(
229            client_end: fidl_next::ClientEnd<P, zx::Channel>,
230        ) -> ClientEnd<P, Transport> {
231            client_end
232        }
233    }
234}