nuclei/io/handle.rs
1//! [`Handle`]: an IO object of std (a socket, a pipe, a file) used
2//! asynchronously.
3//!
4//! A handle owns its IO object and a source, the part its shard's driver
5//! reaches (see `io::source`). The object stays std's own type: `get_ref`
6//! and `Deref` hand it out for its own methods (socket options, addresses),
7//! and the handle's async methods and trait impls do the IO through the
8//! source.
9//!
10//! # The descriptor stays the handle's
11//!
12//! The driver only ever makes calls on a descriptor the handle owns. Before
13//! the object can change or close (`get_mut`, `DerefMut`, `into_inner`,
14//! `into_raw_fd`, drop) the source is retired: its pending work stops and
15//! its registration goes, so a later event for it, or a reuse of the number
16//! by another file, reaches nothing. The next operation registers the
17//! object again, whatever it is by then.
18//!
19//! The driver keeps the descriptor's number, not a borrow of it, so the
20//! number must stay the object's while the handle holds it. That holds
21//! because IO exists only on std's own types (the sockets, `File`), whose
22//! descriptor no method on a shared borrow can close or swap: the only ways
23//! to change it go through `&mut` or ownership, which retire the source
24//! first. A handle over any other `AsFd` type does no IO, so its number is
25//! never used past `new`. A new IO type must keep this: a type whose
26//! descriptor can change behind `&self` must not get IO methods.
27//!
28//! nuclei makes no descriptor of its own for a handle: every call, the
29//! driver's, a ring's and a job's of the blocking pool, runs on the handle's
30//! own descriptor. A call made away from the driver (a file's read or write
31//! on the pool, a `send_file` chunk, a ring's attach) holds a lease on the
32//! object for as long as it may use the number: a handle dropped meanwhile
33//! hands its object to the source, and the last lease closes it; `get_mut`
34//! and `into_inner` wait for the leases. So a process with no descriptor to
35//! spare does everything std's would.
36//!
37//! # Bytes written behind
38//!
39//! A write is accepted into the handle's write-behind, as the kernel
40//! accepts bytes into a socket's buffer, and the driver writes it out. Those
41//! bytes go out whatever happens to the handle, as std's do: `get_mut` and
42//! `into_inner` wait until the kernel took them, and a handle dropped before
43//! that leaves its object to a task, which delivers them (for at most the
44//! runtime's [`write_behind_linger`](crate::runtime::Builder::write_behind_linger))
45//! and then closes it. One whose time ran out gives the rest up, and a TCP
46//! stream's then resets its connection, so the peer never reads a cut stream
47//! as a whole one (`proofs/tla/close`). A runtime that drops waits for those
48//! tasks (see `rt::linger`), and none starts a runtime to run them.
49
50/// How long a dropped handle's unsent bytes may take to reach the kernel, by
51/// default (see [`Builder::write_behind_linger`](crate::runtime::Builder::write_behind_linger)).
52pub(crate) const LINGER: Duration = Duration::from_secs(60);
53
54use core::fmt;
55use core::mem::ManuallyDrop;
56use core::ops::{Deref, DerefMut};
57use std::io;
58#[cfg(unix)]
59use std::os::fd::{AsFd, AsRawFd, BorrowedFd, IntoRawFd, RawFd};
60#[cfg(windows)]
61use std::os::windows::io::{
62 AsHandle, AsRawHandle, AsRawSocket, AsSocket, BorrowedHandle, BorrowedSocket, IntoRawHandle,
63 IntoRawSocket, RawHandle, RawSocket,
64};
65use std::sync::{Arc, OnceLock};
66use std::time::Duration;
67
68use super::raw::Raw;
69use super::source::{Kind, Source};
70use super::sys;
71use crate::fs::file::FileIo;
72
73mod sealed {
74 /// Only nuclei says what an [`IoObject`](super::IoObject) is.
75 pub trait Sealed {}
76}
77
78/// An IO object a [`Handle`] takes over: on unix anything with a file
79/// descriptor; on Windows std's `TcpStream`, `TcpListener`, `UdpSocket` and
80/// `File`.
81pub trait IoObject: sealed::Sealed {
82 /// The descriptor (unix), the socket or the file handle (Windows) the
83 /// handle's IO runs on.
84 #[doc(hidden)]
85 fn raw_io(&self) -> Raw;
86}
87
88#[cfg(unix)]
89impl<T: AsFd> sealed::Sealed for T {}
90
91#[cfg(unix)]
92impl<T: AsFd> IoObject for T {
93 fn raw_io(&self) -> Raw {
94 self.as_fd().as_raw_fd()
95 }
96}
97
98macro_rules! windows_sockets {
99 ($($ty:ty),*) => {$(
100 #[cfg(windows)]
101 impl sealed::Sealed for $ty {}
102
103 #[cfg(windows)]
104 impl IoObject for $ty {
105 fn raw_io(&self) -> Raw {
106 self.as_raw_socket()
107 }
108 }
109 )*};
110}
111
112windows_sockets!(
113 std::net::TcpStream,
114 std::net::TcpListener,
115 std::net::UdpSocket
116);
117
118macro_rules! windows_handles {
119 ($($ty:ty),*) => {$(
120 #[cfg(windows)]
121 impl sealed::Sealed for $ty {}
122
123 #[cfg(windows)]
124 impl IoObject for $ty {
125 fn raw_io(&self) -> Raw {
126 self.as_raw_handle() as usize as Raw
127 }
128 }
129 )*};
130}
131
132windows_handles!(
133 std::fs::File,
134 std::process::ChildStdin,
135 std::process::ChildStdout,
136 std::process::ChildStderr
137);
138
139/// An IO object (a socket, a pipe, a file) whose operations run on nuclei's
140/// shards.
141///
142/// `Send` and `Sync` exactly when the object is.
143///
144/// # std's calls through `Deref`
145///
146/// `Deref` hands the object out for its own methods (socket options,
147/// addresses), and the handle's methods of the same name take their place
148/// where it has them (`connect`, `send`, `linger`, ...). A few of std's
149/// calls reached through it go around the handle, so around the bytes it
150/// read ahead and the ones it wrote behind, or block its shard:
151///
152/// - `shutdown` shuts the halves at once, ahead of the bytes still behind,
153/// which then never reach the peer;
154/// [`shut`](crate::net::TcpStream::shut) waits for them first.
155/// - `set_nonblocking(false)` makes the driver's own calls on the object
156/// block the shard's thread, and every task of the shard with them.
157/// - `take_error` takes the error the socket holds, which the handle's
158/// connect in progress or its next operation would have answered.
159/// - `Read` and `Write` on a shared reference to the object (`&TcpStream`,
160/// `&UnixStream`) read past the bytes read ahead and write ahead of the
161/// bytes written behind, and answer `WouldBlock` where the handle's own
162/// reads and writes wait. Called on the handle with std's traits in scope
163/// (`stream.read(&mut buf)`), they reach the object through `DerefMut`,
164/// after the wait of [`get_mut`](Handle::get_mut).
165///
166/// ```
167/// use std::net::TcpListener;
168/// use nuclei::Handle;
169///
170/// let listener = Handle::new(TcpListener::bind("127.0.0.1:0").unwrap()).unwrap();
171/// assert!(listener.local_addr().unwrap().port() > 0);
172/// ```
173pub struct Handle<T> {
174 io: ManuallyDrop<T>,
175 pub(crate) source: Arc<Source>,
176 /// A regular file's IO state, made on its first operation.
177 pub(crate) file: OnceLock<Arc<FileIo>>,
178}
179
180impl<T: IoObject> Handle<T> {
181 /// Takes `io` over: what kind of object it is (a socket, a file, a
182 /// pipe) decides how its IO runs. On unix it is made non-blocking.
183 ///
184 /// # Errors
185 ///
186 /// When the object cannot be inspected or set up.
187 pub fn new(io: T) -> io::Result<Handle<T>> {
188 let kind = sys::kind_of(io.raw_io())?;
189 Self::adopt(io, kind)
190 }
191
192 /// Takes over `io`, an object of `kind` in blocking mode or not.
193 pub(crate) fn adopt(io: T, kind: Kind) -> io::Result<Handle<T>> {
194 #[cfg(unix)]
195 {
196 let fd = io.raw_io();
197 sys::set_nonblocking(fd)?;
198 if !matches!(kind, Kind::Bytes | Kind::File) {
199 sys::set_nosigpipe(fd);
200 }
201 }
202 Ok(Self::from_nonblocking(io, kind))
203 }
204
205 /// Takes over `io`, an object of `kind` set up already (an accepted
206 /// connection, a socket nuclei made).
207 pub(crate) fn from_nonblocking(io: T, kind: Kind) -> Handle<T> {
208 let source = Source::new(io.raw_io(), kind);
209 Handle {
210 io: ManuallyDrop::new(io),
211 source,
212 file: OnceLock::new(),
213 }
214 }
215
216 /// [`from_nonblocking`](Self::from_nonblocking) for a TCP stream whose kernel counts what
217 /// its reads leave already (see `Source::passes_inq`).
218 #[cfg(any(target_os = "linux", target_os = "android"))]
219 pub(crate) fn from_nonblocking_counting(io: T, kind: Kind) -> Handle<T> {
220 let source = Source::new_counting(io.raw_io(), kind);
221 Handle {
222 io: ManuallyDrop::new(io),
223 source,
224 file: OnceLock::new(),
225 }
226 }
227
228 /// A regular file's IO state, made now on its first use, on a lease of
229 /// the handle's own descriptor.
230 pub(crate) fn file(&self) -> io::Result<&Arc<FileIo>> {
231 if let Some(file) = self.file.get() {
232 return Ok(file);
233 }
234 let file = FileIo::new(self.source().lease()?)?;
235 Ok(self.file.get_or_init(|| file))
236 }
237
238 /// The source, with the object's descriptor in place: after a
239 /// `get_mut` it may be another one.
240 pub(crate) fn source(&self) -> &Arc<Source> {
241 if self.source.is_retired() {
242 self.source.revive(self.io.raw_io());
243 }
244 &self.source
245 }
246}
247
248impl<T> Handle<T> {
249 /// The IO object.
250 pub fn get_ref(&self) -> &T {
251 &self.io
252 }
253
254 /// The IO object, to change. Bytes written behind reach the kernel
255 /// first (this waits for them), then the handle's IO stops, and every
256 /// call still out on its descriptor ends; its next operation takes the
257 /// object as it is then (another descriptor discards what the old one
258 /// left).
259 pub fn get_mut(&mut self) -> &mut T {
260 self.deliver_unsent();
261 self.hand_file_back();
262 self.source.retire();
263 &mut self.io
264 }
265
266 /// The IO object, back from the handle, once the bytes written behind
267 /// reached the kernel and every call still out on its descriptor ended
268 /// (this waits for them). Bytes the handle read ahead and had not handed
269 /// out are dropped with it.
270 pub fn into_inner(mut self) -> T {
271 self.deliver_unsent();
272 self.hand_file_back();
273 let mut this = ManuallyDrop::new(self);
274 this.source.retire();
275 // SAFETY: `this` is never used or dropped again; each field is
276 // moved out once.
277 unsafe {
278 drop(core::ptr::read(&this.source));
279 drop(core::ptr::read(&this.file));
280 ManuallyDrop::take(&mut this.io)
281 }
282 }
283
284 /// A regular file goes back to std where the handle's cursor is: its own
285 /// offset moves there, and the handle's next operation starts from the
286 /// offset std leaves.
287 fn hand_file_back(&mut self) {
288 if let Some(file) = self.file.take()
289 && let Ok(fd) = self.source.raw_fd()
290 {
291 let _ = sys::seek(fd, Some(file.cursor()));
292 }
293 }
294}
295
296impl<T> Handle<T> {
297 /// Waits until the kernel took what the write-behind holds (or the
298 /// runtime's linger passed): on a shard, its other tasks run meanwhile.
299 fn deliver_unsent(&self) {
300 if self.source.has_unsent() {
301 let source = &self.source;
302 let _ = crate::sync::internal::blocking(crate::time::timeout(
303 crate::rt::linger::time(),
304 core::future::poll_fn(|cx| source.poll_drain(cx)),
305 ));
306 }
307 if let Some(file) = self.file.get().filter(|f| f.has_unsent()) {
308 let _ = crate::sync::internal::blocking(crate::time::timeout(
309 crate::rt::linger::time(),
310 core::future::poll_fn(|cx| file.poll_flush(cx)),
311 ));
312 }
313 }
314
315 /// Closes the object now, whatever the write-behind holds: the source's
316 /// close, with no linger (`Source::close`).
317 pub(crate) fn close_now(self) {
318 let mut this = ManuallyDrop::new(self);
319 // SAFETY: `this` is never used or dropped again; each field is moved
320 // out once.
321 let (io, source, file) = unsafe {
322 (
323 ManuallyDrop::take(&mut this.io),
324 core::ptr::read(&this.source),
325 core::ptr::read(&this.file),
326 )
327 };
328 drop(file);
329 source.close(io);
330 }
331}
332
333impl<T> Drop for Handle<T> {
334 /// Never waits: the source closes the object once no call can use its
335 /// descriptor (at once, or as the last lease or the driver lets go of
336 /// it).
337 fn drop(&mut self) {
338 // A file's writes in flight hold leases on its descriptor; what waits
339 // behind them is handed to a task, the file's state with its lease.
340 if let Some(file) = self.file.take().filter(|f| f.has_unsent()) {
341 crate::rt::linger::hand(async move {
342 let _ = crate::time::timeout(
343 crate::rt::linger::time(),
344 core::future::poll_fn(|cx| file.poll_flush(cx)),
345 )
346 .await;
347 });
348 }
349 // SAFETY: taken once, here; the field is never used again.
350 let io = unsafe { ManuallyDrop::take(&mut self.io) };
351 let Some(own) = self
352 .source
353 .raw_fd()
354 .ok()
355 .filter(|_| self.source.has_unsent())
356 else {
357 // The registration goes before the descriptor closes.
358 self.source.close(io);
359 return;
360 };
361 // The object itself stays with a task until the unsent bytes are
362 // out, and closes then: the drain needs no descriptor of its own
363 // (and on Windows, closing a socket would cancel its sends).
364 self.source.linger(own);
365 crate::rt::linger::hand(linger(Arc::clone(&self.source), Kept::new(io), own));
366 }
367}
368
369/// Delivers what a dropped handle's write-behind holds through `kept`, its
370/// object itself (whose descriptor is `own`), then closes it. Bytes still
371/// behind as the drain ends (its time ran out, or it failed) are given up,
372/// and a TCP stream resets: its peer must not read the end of the stream
373/// after a cut one.
374async fn linger(source: Arc<Source>, kept: Kept, own: Raw) {
375 let drain = core::future::poll_fn(|cx| source.poll_drain(cx));
376 let _ = crate::time::timeout(crate::rt::linger::time(), drain).await;
377 if source.has_unsent()
378 && source.kind() == Kind::Tcp
379 && let Err(err) = crate::net::opt::set_linger_time(own, Some(Duration::ZERO))
380 {
381 log::warn!(
382 "a dropped stream's unsent bytes were given up, and its peer is not told: \
383 the stream could not be reset ({err})"
384 );
385 }
386 source.close(kept);
387}
388
389/// A dropped handle's object, kept open while its unsent bytes go out.
390struct Kept {
391 ptr: core::ptr::NonNull<()>,
392 drop: unsafe fn(core::ptr::NonNull<()>),
393}
394
395// SAFETY: a handle does IO only on std's sockets and files, all of them
396// `Send` (see the module docs), and only a handle with bytes written behind
397// lingers; the type is erased because `Drop` cannot say so.
398unsafe impl Send for Kept {}
399
400impl Kept {
401 fn new<T>(io: T) -> Self {
402 /// Drops the `T` boxed at `ptr`.
403 ///
404 /// # Safety
405 ///
406 /// `ptr` came from `Box::<T>::leak`, and is dropped once.
407 unsafe fn drop_boxed<T>(ptr: core::ptr::NonNull<()>) {
408 // SAFETY: the caller's.
409 drop(unsafe { Box::from_raw(ptr.cast::<T>().as_ptr()) });
410 }
411 Self {
412 ptr: core::ptr::NonNull::from(Box::leak(Box::new(io))).cast(),
413 drop: drop_boxed::<T>,
414 }
415 }
416}
417
418impl Drop for Kept {
419 fn drop(&mut self) {
420 // SAFETY: made by `new`, dropped once, here.
421 unsafe { (self.drop)(self.ptr) };
422 }
423}
424
425impl<T> Deref for Handle<T> {
426 type Target = T;
427
428 fn deref(&self) -> &T {
429 &self.io
430 }
431}
432
433impl<T> DerefMut for Handle<T> {
434 /// As [`Handle::get_mut`].
435 fn deref_mut(&mut self) -> &mut T {
436 self.get_mut()
437 }
438}
439
440impl<T: fmt::Debug> fmt::Debug for Handle<T> {
441 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
442 f.debug_struct("Handle").field("io", &*self.io).finish()
443 }
444}
445
446#[cfg(unix)]
447impl<T: AsFd> AsFd for Handle<T> {
448 fn as_fd(&self) -> BorrowedFd<'_> {
449 self.io.as_fd()
450 }
451}
452
453#[cfg(unix)]
454impl<T: AsRawFd> AsRawFd for Handle<T> {
455 fn as_raw_fd(&self) -> RawFd {
456 self.io.as_raw_fd()
457 }
458}
459
460#[cfg(unix)]
461impl<T: IntoRawFd> IntoRawFd for Handle<T> {
462 fn into_raw_fd(self) -> RawFd {
463 self.into_inner().into_raw_fd()
464 }
465}
466
467#[cfg(windows)]
468impl<T: AsSocket> AsSocket for Handle<T> {
469 fn as_socket(&self) -> BorrowedSocket<'_> {
470 self.io.as_socket()
471 }
472}
473
474#[cfg(windows)]
475impl<T: AsRawSocket> AsRawSocket for Handle<T> {
476 fn as_raw_socket(&self) -> RawSocket {
477 self.io.as_raw_socket()
478 }
479}
480
481#[cfg(windows)]
482impl<T: IntoRawSocket> IntoRawSocket for Handle<T> {
483 fn into_raw_socket(self) -> RawSocket {
484 self.into_inner().into_raw_socket()
485 }
486}
487
488#[cfg(windows)]
489impl<T: AsHandle> AsHandle for Handle<T> {
490 fn as_handle(&self) -> BorrowedHandle<'_> {
491 self.io.as_handle()
492 }
493}
494
495#[cfg(windows)]
496impl<T: AsRawHandle> AsRawHandle for Handle<T> {
497 fn as_raw_handle(&self) -> RawHandle {
498 self.io.as_raw_handle()
499 }
500}
501
502#[cfg(windows)]
503impl<T: IntoRawHandle> IntoRawHandle for Handle<T> {
504 fn into_raw_handle(self) -> RawHandle {
505 self.into_inner().into_raw_handle()
506 }
507}