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}