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}