audio_device/runtime/
mod.rs

1//! If any available, this provides handles for various forms of asynchronous
2//! drivers that can be used in combination with audio interfaces.
3
4mod atomic_waker;
5use crate::Result;
6use std::cell::Cell;
7use std::future::Future;
8use std::ptr;
9
10thread_local! {
11    static RUNTIME: Cell<*const Runtime> = Cell::new(ptr::null());
12}
13
14cfg_events_driver! {
15    pub(crate) mod events;
16    #[doc(hidden)]
17    pub use self::events::EventsDriver;
18
19    pub(crate) fn with_events<F, T>(f: F) -> T where F: FnOnce(&EventsDriver) -> T {
20        RUNTIME.with(|rt| {
21            // Safety: we maintain tight control of how and when RUNTIME is
22            // constructed.
23            let rt = unsafe { rt.get().as_ref().expect("missing audio runtime") };
24            f(&rt.events)
25        })
26    }
27}
28
29cfg_poll_driver! {
30    pub(crate) mod poll;
31    #[doc(hidden)]
32    pub use self::poll::{PollDriver, AsyncPoll};
33
34    pub(crate) fn with_poll<F, T>(f: F) -> T where F: FnOnce(&PollDriver) -> T {
35        RUNTIME.with(|rt| {
36            // Safety: we maintain tight control of how and when RUNTIME is
37            // constructed.
38            let rt = unsafe { rt.get().as_ref().expect("missing audio runtime") };
39            f(&rt.poll)
40        })
41    }
42}
43
44/// The audio runtime.
45///
46/// This is necessary to use with asynchronous audio-related APIs.
47///
48/// To run an asynchronous task inside of the audio runtime, we use the
49/// [wrap][Runtime::wrap] function.
50///
51/// # Examples
52///
53/// ```rust,no_run
54/// # async fn task() {}
55/// # #[tokio::main] async fn main() -> anyhow::Result<()> {
56/// let runtime = audio_device::runtime::Runtime::new()?;
57/// runtime.wrap(task()).await;
58/// # Ok(()) }
59/// ```
60pub struct Runtime {
61    #[cfg(feature = "events-driver")]
62    events: self::events::EventsDriver,
63    #[cfg(feature = "poll-driver")]
64    poll: self::poll::PollDriver,
65}
66
67impl Runtime {
68    /// Construct a new audio runtime.
69    pub fn new() -> Result<Self> {
70        Ok(Self {
71            #[cfg(feature = "events-driver")]
72            events: self::events::EventsDriver::new()?,
73            #[cfg(feature = "poll-driver")]
74            poll: self::poll::PollDriver::new()?,
75        })
76    }
77
78    /// Construct a runtime guard that when in scope will provide thread-local
79    /// access to runtime drivers.
80    pub fn enter(&self) -> RuntimeGuard<'_> {
81        let old = RUNTIME.with(|rt| rt.replace(self as *const _));
82
83        RuntimeGuard {
84            _runtime: self,
85            old,
86        }
87    }
88
89    /// Run the given asynchronous task inside of the runtime.
90    ///
91    /// # Examples
92    ///
93    /// ```rust,no_run
94    /// # async fn task() {}
95    /// # #[tokio::main] async fn main() -> anyhow::Result<()> {
96    /// let runtime = audio_device::runtime::Runtime::new()?;
97    /// runtime.wrap(task()).await;
98    /// # Ok(()) }
99    /// ```
100    pub async fn wrap<F>(&self, f: F) -> F::Output
101    where
102        F: Future,
103    {
104        use std::pin::Pin;
105        use std::task::{Context, Poll};
106
107        return GuardFuture(self, f).await;
108
109        struct GuardFuture<'a, F>(&'a Runtime, F);
110
111        impl<'a, F> Future for GuardFuture<'a, F>
112        where
113            F: Future,
114        {
115            type Output = F::Output;
116
117            fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
118                let _guard = self.0.enter();
119                let future = unsafe { Pin::map_unchecked_mut(self, |this| &mut this.1) };
120                future.poll(cx)
121            }
122        }
123    }
124
125    /// Shutdown and join the runtime.
126    pub fn join(self) {
127        #[cfg(feature = "events-driver")]
128        let _ = self.events.join();
129        #[cfg(feature = "poll-driver")]
130        let _ = self.poll.join();
131    }
132}
133
134/// The runtime guard constructed with [Runtime::enter].
135///
136/// Runtime plumbing is available as long as this guard is in scope.
137pub struct RuntimeGuard<'a> {
138    // NB: prevent the guard from outliving the runtime it was constructed from.
139    _runtime: &'a Runtime,
140    old: *const Runtime,
141}
142
143impl Drop for RuntimeGuard<'_> {
144    fn drop(&mut self) {
145        RUNTIME.with(|rt| {
146            rt.set(self.old);
147        })
148    }
149}