Skip to main content

compio_runtime/future/combinator/
mod.rs

1//! Future combinators.
2
3mod cancel;
4mod personality;
5
6use std::borrow::Cow;
7
8pub use cancel::*;
9use compio_driver::Extra;
10use futures_util::Stream;
11pub use personality::*;
12
13use crate::CancelToken;
14
15#[non_exhaustive]
16#[derive(Debug, Default)]
17pub(crate) struct Ext<'a> {
18    personality: Option<u16>,
19    cancel: Option<Cow<'a, CancelToken>>,
20}
21
22impl<'a> Ext<'a> {
23    pub fn to_owned(&self) -> Ext<'static> {
24        Ext {
25            personality: self.personality,
26            cancel: self
27                .cancel
28                .as_ref()
29                .map(|x| Cow::Owned(x.clone().into_owned())),
30        }
31    }
32}
33
34impl<'a> Ext<'a> {
35    pub fn with_personality(&self, personality: u16) -> Self {
36        Self {
37            personality: Some(personality),
38            cancel: self.cancel.clone(),
39        }
40    }
41
42    pub fn with_cancel(&self, token: &'a CancelToken) -> Self {
43        Self {
44            personality: self.personality,
45            cancel: Some(Cow::Borrowed(token)),
46        }
47    }
48
49    pub fn get_cancel(&self) -> Option<&CancelToken> {
50        self.cancel.as_deref()
51    }
52
53    pub fn set_extra(&self, extra: &mut Extra) -> bool {
54        let mut changed = false;
55        if let Some(personality) = self.personality {
56            extra.set_personality(personality);
57            changed = true;
58        }
59        changed
60    }
61}
62
63/// Extension trait for futures.
64///
65/// # Implementation
66///
67/// Extra data are passed down to runtime when the combinators are polled using
68/// a custom [`Waker`], and those data are single-threaded. This means
69/// - when [`Waker`]s are sent to other threads, the data will be lost.
70/// - when using a "sub-executor" like `FuturesUnordered`, which also creates
71///   its own waker, the data will be lost.
72///
73/// So try to keep the path from the wrapped future to runtime clean, something
74/// like this will generally work:
75///
76/// ```rust,ignore
77/// use std::vec::Vec;
78///
79/// use compio::runtime::{FutureExt, CancelToken};
80/// use compio::fs::File;
81///
82/// let file = File::open("/tmp/file");
83/// let cancel = CancelToken::new();
84/// file.read(Vec::with_capacity(1024)).with_cancel(cancel.clone()).await;
85/// ```
86///
87/// [`Waker`]: std::task::Waker
88pub trait FutureExt {
89    /// Sets the personality for this future.
90    ///
91    /// This only takes effect on io-uring drivers and will be ignored on other
92    /// ones.
93    fn with_personality(self, personality: u16) -> WithPersonality<Self>
94    where
95        Self: Sized,
96    {
97        WithPersonality::new(self, personality)
98    }
99
100    /// Sets the cancel token for this future.
101    ///
102    /// If multiple [`CancelToken`]s are set, the innermost one (the one being
103    /// polled last) will take precedence.
104    fn with_cancel(self, token: CancelToken) -> WithCancel<Self>
105    where
106        Self: Sized,
107    {
108        WithCancel::new(self, token)
109    }
110}
111
112impl<F: Future + ?Sized> FutureExt for F {}
113
114/// Extension trait for streams.
115///
116/// # Implementation
117///
118/// Extra data are passed down to runtime when the combinators are polled using
119/// a custom [`Waker`], and those data are single-threaded. This means
120/// - when [`Waker`]s are sent to other threads, the data will be lost.
121/// - when using a "sub-executor" like `FuturesUnordered`, which also creates
122///   its own waker, the data will be lost.
123///
124/// So try to keep the path from the wrapped stream to runtime clean, something
125/// like this will generally work:
126///
127/// ```rust,ignore
128/// use std::vec::Vec;
129///
130/// use compio::runtime::{StreamExt, CancelToken};
131/// use compio::net::TcpStream;
132///
133/// let input = TcpStream::connect("127.0.0.1:8000").await.unwrap();
134/// let cancel = CancelToken::new();
135/// let mut input_stream = input.read_multi(0).with_cancel(cancel.clone());
136/// while let Some(buf) = input_stream.next().await {
137///     let _ = buf.unwrap();
138/// }
139/// ```
140///
141/// [`Waker`]: std::task::Waker
142pub trait StreamExt {
143    /// Sets the personality for this stream.
144    ///
145    /// This only takes effect on io-uring drivers and will be ignored on other
146    /// ones.
147    fn with_personality(self, personality: u16) -> WithPersonality<Self>
148    where
149        Self: Sized,
150    {
151        WithPersonality::new(self, personality)
152    }
153
154    /// Sets the cancel token for this stream.
155    ///
156    /// If multiple [`CancelToken`]s are set, the innermost one (the one being
157    /// polled last) will take precedence.
158    fn with_cancel(self, token: CancelToken) -> WithCancel<Self>
159    where
160        Self: Sized,
161    {
162        WithCancel::new(self, token)
163    }
164}
165
166impl<F: Stream + ?Sized> StreamExt for F {}