Skip to main content

compio_compat/
lib.rs

1//! Runtime-compatibility layers for compio.
2//!
3//! This crate provides a compatibility layer for compio's runtime, allowing it
4//! to be used with different underlying event loop implementations, e.g.,
5//! `tokio` or `smol`.
6
7#![cfg_attr(docsrs, feature(doc_cfg))]
8#![warn(missing_docs)]
9#![deny(rustdoc::broken_intra_doc_links)]
10#![doc(
11    html_logo_url = "https://github.com/compio-rs/compio-logo/raw/refs/heads/master/generated/colored-bold.svg"
12)]
13#![doc(
14    html_favicon_url = "https://github.com/compio-rs/compio-logo/raw/refs/heads/master/generated/colored-bold.svg"
15)]
16
17use std::{
18    io,
19    ops::Deref,
20    task::{Context, Poll},
21    time::Duration,
22};
23
24use compio_log::error;
25use compio_runtime::{Runtime, SpawnMeta, console};
26use mod_use::mod_use;
27
28mod_use![sys];
29
30/// A compatibility layer for [`Runtime`]. It is driven by the underlying
31/// [`Adapter`].
32pub struct RuntimeCompat<A> {
33    runtime: A,
34}
35
36impl<A: Adapter> RuntimeCompat<A> {
37    /// Creates a new [`RuntimeCompat`] with the given runtime.
38    pub fn new(runtime: Runtime) -> io::Result<Self> {
39        let runtime = A::new(runtime)?;
40        Ok(Self { runtime })
41    }
42
43    /// Executes the given future on the runtime, driving it to completion.
44    ///
45    /// This is a plain `fn` so that the console can report the caller:
46    /// `#[track_caller]` is a no-op on an `async fn`.
47    #[track_caller]
48    pub fn execute<F: Future>(&self, f: F) -> impl Future<Output = F::Output> {
49        // The console has no kind of its own for this, and reports it the way
50        // it reports a future the runtime blocks on, so the name is what tells
51        // the two apart. Captured before the future, which is where the caller
52        // is lost.
53        let f = console::instrument_execute(SpawnMeta::capture().named("execute"), f);
54
55        self.drive(f)
56    }
57
58    async fn drive<F: Future>(&self, f: F) -> F::Output {
59        let waker = self.runtime.waker();
60        let mut context = Context::from_waker(&waker);
61        let mut future = std::pin::pin!(f);
62        loop {
63            if let Poll::Ready(result) = self.runtime.enter(|| future.as_mut().poll(&mut context)) {
64                self.runtime.enter(|| self.runtime.run());
65                return result;
66            }
67
68            let mut remaining_tasks = self.runtime.enter(|| self.runtime.run());
69
70            remaining_tasks |= self.runtime.flush();
71
72            let timeout = if remaining_tasks {
73                Some(Duration::ZERO)
74            } else {
75                self.runtime.current_timeout()
76            };
77
78            match self.runtime.wait(timeout).await {
79                Ok(_) => {}
80                Err(e)
81                    if matches!(
82                        e.kind(),
83                        io::ErrorKind::TimedOut | io::ErrorKind::Interrupted
84                    ) => {}
85                Err(e) => panic!("failed to wait for driver: {e:?}"),
86            }
87
88            if let Err(e) = self.runtime.clear() {
89                error!("failed to clear notifier: {e:?}");
90            }
91
92            self.runtime.poll_with(Some(Duration::ZERO));
93        }
94    }
95}
96
97impl<A: Adapter> Deref for RuntimeCompat<A> {
98    type Target = Runtime;
99
100    fn deref(&self) -> &Self::Target {
101        &self.runtime
102    }
103}