Skip to main content

tokio_uring/runtime/driver/op/
mod.rs

1use std::future::Future;
2use std::io;
3use std::marker::PhantomData;
4use std::pin::Pin;
5use std::task::{Context, Poll, Waker};
6
7use io_uring::{cqueue, squeue};
8
9mod slab_list;
10
11use slab::Slab;
12use slab_list::{SlabListEntry, SlabListIndices};
13
14use crate::runtime::{driver, CONTEXT};
15
16/// A SlabList is used to hold unserved completions.
17///
18/// This is relevant to multi-completion Operations,
19/// which require an unknown number of CQE events to be
20/// captured before completion.
21pub(crate) type Completion = SlabListEntry<CqeResult>;
22
23/// An unsubmitted oneshot operation.
24pub struct UnsubmittedOneshot<D: 'static, T: OneshotOutputTransform<StoredData = D>> {
25    stable_data: D,
26    post_op: T,
27    sqe: squeue::Entry,
28}
29
30impl<D, T: OneshotOutputTransform<StoredData = D>> UnsubmittedOneshot<D, T> {
31    /// Construct a new operation for later submission.
32    pub fn new(stable_data: D, post_op: T, sqe: squeue::Entry) -> Self {
33        Self {
34            stable_data,
35            post_op,
36            sqe,
37        }
38    }
39
40    /// Submit an operation to the driver for batched entry to the kernel.
41    pub fn submit(self) -> InFlightOneshot<D, T> {
42        let handle = CONTEXT
43            .with(|x| x.handle())
44            .expect("Could not submit op; not in runtime context");
45
46        self.submit_with_driver(&handle)
47    }
48
49    fn submit_with_driver(self, driver: &driver::Handle) -> InFlightOneshot<D, T> {
50        let index = driver.submit_op_2(self.sqe);
51
52        let driver = driver.into();
53
54        let inner = InFlightOneshotInner {
55            index,
56            driver,
57            stable_data: self.stable_data,
58            post_op: self.post_op,
59        };
60
61        InFlightOneshot { inner: Some(inner) }
62    }
63}
64
65/// An in-progress oneshot operation which can be polled for completion.
66pub struct InFlightOneshot<D: 'static, T: OneshotOutputTransform<StoredData = D>> {
67    inner: Option<InFlightOneshotInner<D, T>>,
68}
69
70struct InFlightOneshotInner<D, T: OneshotOutputTransform<StoredData = D>> {
71    driver: driver::WeakHandle,
72    index: usize,
73    stable_data: D,
74    post_op: T,
75}
76
77impl<D: Unpin, T: OneshotOutputTransform<StoredData = D> + Unpin> Future for InFlightOneshot<D, T> {
78    type Output = T::Output;
79
80    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
81        let this = self.get_mut();
82
83        let inner = this
84            .inner
85            .as_mut()
86            .expect("Cannot poll already-completed operation");
87
88        let index = inner.index;
89
90        let upgraded = inner
91            .driver
92            .upgrade()
93            .expect("Failed to poll op: driver no longer exists");
94
95        let cqe = ready!(upgraded.poll_op_2(index, cx));
96
97        let inner = this.inner.take().unwrap();
98
99        Poll::Ready(
100            inner
101                .post_op
102                .transform_oneshot_output(inner.stable_data, cqe),
103        )
104    }
105}
106
107impl<D: 'static, T: OneshotOutputTransform<StoredData = D>> Drop for InFlightOneshot<D, T> {
108    fn drop(&mut self) {
109        if let Some(inner) = self.inner.take() {
110            if let Some(driver) = inner.driver.upgrade() {
111                driver.remove_op_2(inner.index, inner.stable_data)
112            }
113        }
114    }
115}
116
117/// Transforms the output of a oneshot operation into a more user-friendly format.
118pub trait OneshotOutputTransform {
119    /// The final output after the transformation.
120    type Output;
121    /// The stored data within the op.
122    type StoredData;
123    /// Transform the stored data and the cqe into the final output.
124    fn transform_oneshot_output(self, data: Self::StoredData, cqe: cqueue::Entry) -> Self::Output;
125}
126
127/// In-flight operation
128pub(crate) struct Op<T: 'static, CqeType = SingleCQE> {
129    driver: driver::WeakHandle,
130    // Operation index in the slab
131    index: usize,
132
133    // Per-operation data
134    data: Option<T>,
135
136    // CqeType marker
137    _cqe_type: PhantomData<CqeType>,
138}
139
140/// A Marker for Ops which expect only a single completion event
141pub(crate) struct SingleCQE;
142
143/// A Marker for Operations will process multiple completion events,
144/// which combined resolve to a single Future value
145pub(crate) struct MultiCQEFuture;
146
147pub(crate) trait Completable {
148    type Output;
149    /// `complete` will be called for cqe's do not have the `more` flag set
150    fn complete(self, cqe: CqeResult) -> Self::Output;
151}
152
153pub(crate) trait Updateable: Completable {
154    /// Update will be called for cqe's which have the `more` flag set.
155    /// The Op should update any internal state as required.
156    fn update(&mut self, cqe: CqeResult);
157}
158
159#[allow(dead_code)]
160pub(crate) enum Lifecycle {
161    /// The operation has been submitted to uring and is currently in-flight
162    Submitted,
163
164    /// The submitter is waiting for the completion of the operation
165    Waiting(Waker),
166
167    /// The submitter no longer has interest in the operation result. The state
168    /// must be passed to the driver and held until the operation completes.
169    Ignored(Box<dyn std::any::Any>),
170
171    /// The operation has completed with a single cqe result
172    Completed(cqueue::Entry),
173
174    /// One or more completion results have been recieved
175    /// This holds the indices uniquely identifying the list within the slab
176    CompletionList(SlabListIndices),
177}
178
179/// A single CQE entry
180pub(crate) struct CqeResult {
181    pub(crate) result: io::Result<u32>,
182    pub(crate) flags: u32,
183}
184
185impl From<cqueue::Entry> for CqeResult {
186    fn from(cqe: cqueue::Entry) -> Self {
187        let res = cqe.result();
188        let flags = cqe.flags();
189        let result = if res >= 0 {
190            Ok(res as u32)
191        } else {
192            Err(io::Error::from_raw_os_error(-res))
193        };
194        CqeResult { result, flags }
195    }
196}
197
198impl<T, CqeType> Op<T, CqeType> {
199    /// Create a new operation
200    pub(super) fn new(driver: driver::WeakHandle, data: T, index: usize) -> Self {
201        Op {
202            driver,
203            index,
204            data: Some(data),
205            _cqe_type: PhantomData,
206        }
207    }
208
209    pub(super) fn index(&self) -> usize {
210        self.index
211    }
212
213    pub(super) fn take_data(&mut self) -> Option<T> {
214        self.data.take()
215    }
216
217    pub(super) fn insert_data(&mut self, data: T) {
218        self.data = Some(data);
219    }
220}
221
222impl<T> Future for Op<T, SingleCQE>
223where
224    T: Unpin + 'static + Completable,
225{
226    type Output = T::Output;
227
228    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
229        self.driver
230            .upgrade()
231            .expect("Not in runtime context")
232            .poll_op(self.get_mut(), cx)
233    }
234}
235
236impl<T> Future for Op<T, MultiCQEFuture>
237where
238    T: Unpin + 'static + Completable + Updateable,
239{
240    type Output = T::Output;
241
242    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
243        self.driver
244            .upgrade()
245            .expect("Not in runtime context")
246            .poll_multishot_op(self.get_mut(), cx)
247    }
248}
249
250/// The operation may have pending cqe's not yet processed.
251/// To manage this, the lifecycle associated with the Op may if required
252/// be placed in LifeCycle::Ignored state to handle cqe's which arrive after
253/// the Op has been dropped.
254impl<T, CqeType> Drop for Op<T, CqeType> {
255    fn drop(&mut self) {
256        self.driver
257            .upgrade()
258            .expect("Not in runtime context")
259            .remove_op(self)
260    }
261}
262
263impl Lifecycle {
264    pub(crate) fn complete(
265        &mut self,
266        completions: &mut Slab<Completion>,
267        cqe: cqueue::Entry,
268    ) -> bool {
269        use std::mem;
270
271        match mem::replace(self, Lifecycle::Submitted) {
272            x @ Lifecycle::Submitted | x @ Lifecycle::Waiting(..) => {
273                if io_uring::cqueue::more(cqe.flags()) {
274                    let mut list = SlabListIndices::new().into_list(completions);
275                    list.push(cqe.into());
276                    *self = Lifecycle::CompletionList(list.into_indices());
277                } else {
278                    *self = Lifecycle::Completed(cqe);
279                }
280                if let Lifecycle::Waiting(waker) = x {
281                    // waker is woken to notify cqe has arrived
282                    // Note: Maybe defer calling until cqe with !`more` flag set?
283                    waker.wake();
284                }
285                false
286            }
287
288            lifecycle @ Lifecycle::Ignored(..) => {
289                if io_uring::cqueue::more(cqe.flags()) {
290                    // Not yet complete. The Op has been dropped, so we can drop the CQE
291                    // but we must keep the lifecycle alive until no more CQE's expected
292                    *self = lifecycle;
293                    false
294                } else {
295                    // This Op has completed, we can drop
296                    true
297                }
298            }
299
300            Lifecycle::Completed(..) => {
301                // Completions with more flag set go straight onto the slab,
302                // and are handled in Lifecycle::CompletionList.
303                // To construct Lifecycle::Completed, a CQE with `more` flag unset was received
304                // we shouldn't be receiving another.
305                unreachable!("invalid operation state")
306            }
307
308            Lifecycle::CompletionList(indices) => {
309                // A completion list may contain CQE's with and without `more` flag set.
310                // Only the final one may have `more` unset, although we don't check.
311                let mut list = indices.into_list(completions);
312                list.push(cqe.into());
313                *self = Lifecycle::CompletionList(list.into_indices());
314                false
315            }
316        }
317    }
318}