Skip to main content

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}