Skip to main content

yash_env/system/
select.rs

1// This file is part of yash, an extended POSIX shell.
2// Copyright (C) 2021 WATANABE Yuki
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program.  If not, see <https://www.gnu.org/licenses/>.
16
17//! Items related to the `select` system call
18
19#[cfg(doc)]
20use super::Concurrent;
21use super::Result;
22use super::Sigmask;
23use crate::io::Fd;
24use std::ffi::c_int;
25use std::fmt::Debug;
26use std::future::Future;
27use std::rc::Rc;
28use std::time::Duration;
29
30/// Trait for representing a set of file descriptors (FDs)
31///
32/// This is an abstraction over the `fd_set` type used in the `select` system
33/// call. It represents a set of [`Fd`]s that can be monitored for events such
34/// as readability or writability.
35///
36/// As per POSIX, an `fd_set` can only contain FDs in the range of `0` to
37/// `FD_SETSIZE - 1`. The [`MAX_FD`](Self::MAX_FD) associated constant in this
38/// trait represents the maximum FD that can be stored in the set.
39pub trait FdSet: Clone + Default + 'static {
40    /// The maximum FD that can be stored in the set. This corresponds to
41    /// `FD_SETSIZE - 1` in C libraries. The exact value may depend on the
42    /// implementation.
43    const MAX_FD: Fd;
44
45    /// Creates a new, empty FD set.
46    ///
47    /// The provided implementation simply calls [`Default::default`].
48    #[inline(always)]
49    #[must_use]
50    fn new() -> Self {
51        Default::default()
52    }
53
54    /// Adds an FD to the set.
55    ///
56    /// If the provided FD is greater than [`MAX_FD`](Self::MAX_FD) or negative,
57    /// it will be silently ignored.
58    fn insert(&mut self, fd: Fd);
59
60    /// Removes an FD from the set.
61    ///
62    /// If the provided FD is greater than [`MAX_FD`](Self::MAX_FD) or negative,
63    /// it will be silently ignored.
64    fn remove(&mut self, fd: Fd);
65
66    /// Checks if an FD is in the set.
67    ///
68    /// If the provided FD is greater than [`MAX_FD`](Self::MAX_FD) or negative,
69    /// this function will return `false`.
70    fn contains(&self, fd: Fd) -> bool;
71
72    /// Creates a new FD set from an iterator of FDs.
73    ///
74    /// This is a convenience method that creates a new FD set and inserts all
75    /// the FDs from the provided iterator into it. If any FD in the iterator is
76    /// greater than [`MAX_FD`](Self::MAX_FD) or negative, it will be silently
77    /// ignored.
78    #[must_use]
79    fn from_fds<I>(iter: I) -> Self
80    where
81        I: IntoIterator<Item = Fd>,
82    {
83        let mut set = Self::new();
84        for fd in iter {
85            set.insert(fd);
86        }
87        set
88    }
89}
90
91/// Trait for performing the `select` operation
92///
93/// This trait provides the `select` method, which represents the `select`
94/// system call. This trait is different from the
95/// [same-named trait in the `concurrency` submodule](super::concurrency::Select),
96/// which provides a higher-level interface for waiting on multiple events. The
97/// `Select` trait in this module is a lower-level interface that directly
98/// represents the behavior of the `select` system call. It is used by the
99/// `Select` trait in the `concurrency` submodule to implement its
100/// functionality.
101pub trait Select: Sigmask {
102    /// The type of the file descriptor set used in the `select` method
103    ///
104    /// This type is used in the [`select`](Self::select) method to specify
105    /// file descriptor sets. The exact type will depend on the implementation
106    /// of the trait.
107    type FdSet: FdSet + Debug;
108
109    /// Waits for a next event.
110    ///
111    /// In a typical configuration, this trait is not used directly. Instead,
112    /// it is used by [`Concurrent`] to implement asynchronous I/O, signal
113    /// handling, and timer functions.
114    ///
115    /// This function blocks the calling thread until one of the following
116    /// conditions is met:
117    ///
118    /// - An FD in `readers` becomes ready for reading.
119    /// - An FD in `writers` becomes ready for writing.
120    /// - The specified `timeout` duration has passed.
121    /// - A signal handler catches a signal.
122    ///
123    /// When this function returns an `Ok`, FDs that are not ready for reading
124    /// and writing are removed from `readers` and `writers`, respectively. The
125    /// return value will be the number of FDs left in `readers` and `writers`.
126    ///
127    /// If `readers` and `writers` contain an FD that is not open for reading
128    /// and writing, respectively, this function will fail with `EBADF`. In this
129    /// case, you should remove the FD from `readers` and `writers` and try
130    /// again.
131    ///
132    /// If `signal_mask` is `Some` list of signals, it is used as the signal
133    /// blocking mask while waiting and restored when the function returns.
134    ///
135    /// The return type is a future so that
136    /// [virtual systems](crate::system::virtual) can simulate the blocking
137    /// behavior of `select` without blocking the entire process. The future
138    /// will be ready when one of the above conditions is met. The future may
139    /// also return `Pending` if the virtual process is suspended by a signal.
140    /// In a [real system](super::real), this function does not work
141    /// asynchronously and returns a ready `Future` with the result of the
142    /// underlying system call. See the [module-level documentation](super) for
143    /// details.
144    fn select<'a>(
145        &self,
146        readers: &'a mut Self::FdSet,
147        writers: &'a mut Self::FdSet,
148        timeout: Option<Duration>,
149        signal_mask: Option<&Self::Sigset>,
150    ) -> impl Future<Output = Result<c_int>> + use<'a, Self>;
151}
152
153/// Delegates the `Select` trait to the contained instance of `S`
154impl<S: Select> Select for Rc<S> {
155    type FdSet = S::FdSet;
156
157    #[inline]
158    fn select<'a>(
159        &self,
160        readers: &'a mut S::FdSet,
161        writers: &'a mut S::FdSet,
162        timeout: Option<Duration>,
163        signal_mask: Option<&S::Sigset>,
164    ) -> impl Future<Output = Result<c_int>> + use<'a, S> {
165        (self as &S).select(readers, writers, timeout, signal_mask)
166    }
167}