Skip to main content

tokio/runtime/io/
registration.rs

1#![cfg_attr(not(feature = "net"), allow(dead_code))]
2
3use crate::io::interest::Interest;
4use crate::runtime::io::{Direction, Handle, ReadyEvent, ScheduledIo};
5use crate::runtime::scheduler;
6
7use crate::loom::sync::Arc;
8use mio::event::Source;
9use std::io;
10use std::task::{ready, Context, Poll};
11
12cfg_io_driver! {
13    /// Associates an I/O resource with the reactor instance that drives it.
14    ///
15    /// A registration represents an I/O resource registered with a Reactor such
16    /// that it will receive task notifications on readiness. This is the lowest
17    /// level API for integrating with a reactor.
18    ///
19    /// The association between an I/O resource is made by calling
20    /// [`new_with_interest_and_handle`].
21    /// Once the association is established, it remains established until the
22    /// registration instance is dropped.
23    ///
24    /// A registration instance represents two separate readiness streams. One
25    /// for the read readiness and one for write readiness. These streams are
26    /// independent and can be consumed from separate tasks.
27    ///
28    /// **Note**: while `Registration` is `Sync`, the caller must ensure that
29    /// there are at most two tasks that use a registration instance
30    /// concurrently. One task for [`poll_read_ready`] and one task for
31    /// [`poll_write_ready`]. While violating this requirement is "safe" from a
32    /// Rust memory safety point of view, it will result in unexpected behavior
33    /// in the form of lost notifications and tasks hanging.
34    ///
35    /// ## Platform-specific events
36    ///
37    /// `Registration` also allows receiving platform-specific `mio::Ready`
38    /// events. These events are included as part of the read readiness event
39    /// stream. The write readiness event stream is only for `Ready::writable()`
40    /// events.
41    ///
42    /// [`new_with_interest_and_handle`]: method@Self::new_with_interest_and_handle
43    /// [`poll_read_ready`]: method@Self::poll_read_ready`
44    /// [`poll_write_ready`]: method@Self::poll_write_ready`
45    #[derive(Debug)]
46    pub(crate) struct Registration {
47        /// Handle to the associated runtime.
48        ///
49        /// TODO: this can probably be moved into `ScheduledIo`.
50        handle: scheduler::Handle,
51
52        /// Reference to state stored by the driver.
53        shared: Arc<ScheduledIo>,
54    }
55}
56
57unsafe impl Send for Registration {}
58unsafe impl Sync for Registration {}
59
60// ===== impl Registration =====
61
62impl Registration {
63    /// Registers the I/O resource with the reactor for the provided handle, for
64    /// a specific `Interest`. This does not add `hup` or `error` so if you are
65    /// interested in those states, you will need to add them to the readiness
66    /// state passed to this function.
67    ///
68    /// # Return
69    ///
70    /// - `Ok` if the registration happened successfully
71    /// - `Err` if an error was encountered during registration
72    #[track_caller]
73    pub(crate) fn new_with_interest_and_handle(
74        io: &mut impl Source,
75        interest: Interest,
76        handle: scheduler::Handle,
77    ) -> io::Result<Registration> {
78        let shared = handle.driver().io().add_source(io, interest)?;
79
80        Ok(Registration { handle, shared })
81    }
82
83    /// Deregisters the I/O resource from the reactor it is associated with.
84    ///
85    /// This function must be called before the I/O resource associated with the
86    /// registration is dropped.
87    ///
88    /// Note that deregistering does not guarantee that the I/O resource can be
89    /// registered with a different reactor. Some I/O resource types can only be
90    /// associated with a single reactor instance for their lifetime.
91    ///
92    /// # Return
93    ///
94    /// If the deregistration was successful, `Ok` is returned. Any calls to
95    /// `Reactor::turn` that happen after a successful call to `deregister` will
96    /// no longer result in notifications getting sent for this registration.
97    ///
98    /// `Err` is returned if an error is encountered.
99    pub(crate) fn deregister(&mut self, io: &mut impl Source) -> io::Result<()> {
100        self.handle().deregister_source(&self.shared, io)
101    }
102
103    pub(crate) fn clear_readiness(&self, event: ReadyEvent) {
104        self.shared.clear_readiness(event);
105    }
106
107    /// Marks the resource ready without waiting for the driver's first event.
108    /// Used for sockets whose state is known when they are created: accepted
109    /// sockets are writable and usually already hold the peer's first bytes
110    /// (under load that first event can queue behind every established
111    /// connection's events), and both ends of a fresh `pair()` are writable.
112    /// A wrong guess costs one `WouldBlock`, which clears the readiness again.
113    #[cfg(feature = "net")]
114    pub(crate) fn assume_ready(&self, ready: crate::io::Ready) {
115        self.shared.assume_ready(ready);
116    }
117
118    // Uses the poll path, requiring the caller to ensure mutual exclusion for
119    // correctness. Only the last task to call this function is notified.
120    pub(crate) fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<ReadyEvent>> {
121        self.poll_ready(cx, Direction::Read)
122    }
123
124    // Uses the poll path, requiring the caller to ensure mutual exclusion for
125    // correctness. Only the last task to call this function is notified.
126    pub(crate) fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<ReadyEvent>> {
127        self.poll_ready(cx, Direction::Write)
128    }
129
130    // Uses the poll path, requiring the caller to ensure mutual exclusion for
131    // correctness. Only the last task to call this function is notified.
132    #[cfg(not(all(target_os = "wasi", target_env = "p1")))]
133    pub(crate) fn poll_read_io<R>(
134        &self,
135        cx: &mut Context<'_>,
136        f: impl FnMut() -> io::Result<R>,
137    ) -> Poll<io::Result<R>> {
138        self.poll_io(cx, Direction::Read, f)
139    }
140
141    // Uses the poll path, requiring the caller to ensure mutual exclusion for
142    // correctness. Only the last task to call this function is notified.
143    pub(crate) fn poll_write_io<R>(
144        &self,
145        cx: &mut Context<'_>,
146        f: impl FnMut() -> io::Result<R>,
147    ) -> Poll<io::Result<R>> {
148        self.poll_io(cx, Direction::Write, f)
149    }
150
151    /// Polls for events on the I/O resource's `direction` readiness stream.
152    ///
153    /// If called with a task context, notify the task when a new event is
154    /// received.
155    fn poll_ready(
156        &self,
157        cx: &mut Context<'_>,
158        direction: Direction,
159    ) -> Poll<io::Result<ReadyEvent>> {
160        ready!(crate::trace::trace_leaf(cx));
161        // Keep track of task budget
162        let coop = ready!(crate::task::coop::poll_proceed(cx));
163        let ev = ready!(self.shared.poll_readiness(cx, direction));
164
165        if ev.is_shutdown {
166            return Poll::Ready(Err(gone()));
167        }
168
169        coop.made_progress();
170        Poll::Ready(Ok(ev))
171    }
172
173    fn poll_io<R>(
174        &self,
175        cx: &mut Context<'_>,
176        direction: Direction,
177        mut f: impl FnMut() -> io::Result<R>,
178    ) -> Poll<io::Result<R>> {
179        loop {
180            let ev = ready!(self.poll_ready(cx, direction))?;
181
182            match f() {
183                Ok(ret) => {
184                    return Poll::Ready(Ok(ret));
185                }
186                Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
187                    self.clear_readiness(ev);
188                }
189                Err(e) => return Poll::Ready(Err(e)),
190            }
191        }
192    }
193
194    pub(crate) fn try_io<R>(
195        &self,
196        interest: Interest,
197        f: impl FnOnce() -> io::Result<R>,
198    ) -> io::Result<R> {
199        let ev = self.shared.ready_event(interest);
200
201        // Don't attempt the operation if the resource is not ready.
202        if ev.ready.is_empty() {
203            return Err(io::ErrorKind::WouldBlock.into());
204        }
205
206        match f() {
207            Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
208                self.clear_readiness(ev);
209                Err(io::ErrorKind::WouldBlock.into())
210            }
211            res => res,
212        }
213    }
214
215    pub(crate) async fn readiness(&self, interest: Interest) -> io::Result<ReadyEvent> {
216        let ev = self.shared.readiness(interest).await;
217
218        if ev.is_shutdown {
219            return Err(gone());
220        }
221
222        Ok(ev)
223    }
224
225    pub(crate) async fn async_io<R>(
226        &self,
227        interest: Interest,
228        mut f: impl FnMut() -> io::Result<R>,
229    ) -> io::Result<R> {
230        loop {
231            let event = self.readiness(interest).await?;
232
233            let coop = std::future::poll_fn(crate::task::coop::poll_proceed).await;
234
235            match f() {
236                Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
237                    self.clear_readiness(event);
238                }
239                x => {
240                    coop.made_progress();
241                    return x;
242                }
243            }
244        }
245    }
246
247    fn handle(&self) -> &Handle {
248        self.handle.driver().io()
249    }
250}
251
252impl Drop for Registration {
253    fn drop(&mut self) {
254        // It is possible for a cycle to be created between wakers stored in
255        // `ScheduledIo` instances and `Arc<driver::Inner>`. To break this
256        // cycle, wakers are cleared. This is an imperfect solution as it is
257        // possible to store a `Registration` in a waker. In this case, the
258        // cycle would remain.
259        //
260        // See tokio-rs/tokio#3481 for more details.
261        self.shared.clear_wakers();
262    }
263}
264
265fn gone() -> io::Error {
266    io::Error::other(crate::util::error::RUNTIME_SHUTTING_DOWN_ERROR)
267}