yash_env/system/concurrency/
signal.rs1use super::Concurrent;
20use crate::signal::Number;
21use crate::system::{Disposition, Errno, Sigaction, Sigmask, SigmaskOp};
22use crate::trap::SignalSystem;
23use std::rc::Rc;
24
25impl<S> SignalSystem for Rc<Concurrent<S>>
30where
31 S: Sigmask + Sigaction,
32{
33 fn get_disposition(&self, signal: Number) -> Result<Disposition, Errno> {
39 self.inner.get_sigaction(signal)
40 }
41
42 fn set_disposition(
54 &self,
55 signal: Number,
56 disposition: Disposition,
57 ) -> impl Future<Output = Result<Disposition, Errno>> + use<S> {
58 let this = Rc::clone(self);
59 async move {
60 if disposition == Disposition::Catch {
61 this.update_sigmask_and_select_mask(SigmaskOp::Add, signal)
64 .await?;
65 }
66
67 let old_action = this.inner.sigaction(signal, disposition)?;
68
69 if disposition != Disposition::Catch {
70 this.update_sigmask_and_select_mask(SigmaskOp::Remove, signal)
73 .await?;
74 }
75
76 Ok(old_action)
77 }
78 }
79}
80
81impl<S> Concurrent<S>
82where
83 S: Sigmask,
84{
85 async fn update_sigmask_and_select_mask(
88 &self,
89 op: SigmaskOp,
90 signal: Number,
91 ) -> Result<(), Errno> {
92 let mut old_mask = Vec::new();
93 self.inner
94 .sigmask(Some((op, &[signal])), Some(&mut old_mask))
95 .await?;
96
97 self.state
98 .borrow_mut()
99 .select_mask
100 .get_or_insert(old_mask)
101 .retain(|&s| s != signal);
102 Ok(())
103 }
104}
105
106#[cfg(test)]
107mod tests {
108 use super::*;
109 use crate::job::{ProcessResult, ProcessState};
110 use crate::system::SendSignal as _;
111 use crate::system::r#virtual::{SIGQUIT, SIGTERM, SIGUSR1, VirtualSystem};
112 use futures_util::FutureExt as _;
113 use std::num::NonZero;
114
115 #[test]
116 fn setting_disposition_from_default_to_catch() {
117 let inner = VirtualSystem::new();
118 let system = Rc::new(Concurrent::new(inner.clone()));
119 let result = system
120 .set_disposition(SIGTERM, Disposition::Catch)
121 .now_or_never()
122 .unwrap();
123 assert_eq!(result, Ok(Disposition::Default));
124 assert_eq!(system.get_disposition(SIGTERM), Ok(Disposition::Catch));
125
126 inner.raise(SIGTERM).now_or_never().unwrap().unwrap();
128 assert_eq!(inner.current_process().state(), ProcessState::Running);
130 }
131
132 #[test]
133 fn setting_disposition_from_default_to_ignore() {
134 let inner = VirtualSystem::new();
135 let system = Rc::new(Concurrent::new(inner.clone()));
136 let result = system
137 .set_disposition(SIGTERM, Disposition::Ignore)
138 .now_or_never()
139 .unwrap();
140 assert_eq!(result, Ok(Disposition::Default));
141 assert_eq!(system.get_disposition(SIGTERM), Ok(Disposition::Ignore));
142
143 inner.raise(SIGTERM).now_or_never().unwrap().unwrap();
145 assert_eq!(inner.current_process().state(), ProcessState::Running);
146 }
147
148 #[test]
149 fn setting_disposition_from_ignore_to_catch() {
150 let system = Rc::new(Concurrent::new(VirtualSystem::new()));
151 system
152 .set_disposition(SIGQUIT, Disposition::Ignore)
153 .now_or_never()
154 .unwrap()
155 .unwrap();
156
157 let result = system
158 .set_disposition(SIGQUIT, Disposition::Catch)
159 .now_or_never()
160 .unwrap();
161 assert_eq!(result, Ok(Disposition::Ignore));
162 assert_eq!(system.get_disposition(SIGQUIT), Ok(Disposition::Catch));
163 }
164
165 #[test]
166 fn setting_disposition_from_catch_to_default() {
167 let inner = VirtualSystem::new();
168 let system = Rc::new(Concurrent::new(inner.clone()));
169 system
170 .set_disposition(SIGQUIT, Disposition::Catch)
171 .now_or_never()
172 .unwrap()
173 .unwrap();
174 system.raise(SIGQUIT).now_or_never().unwrap().unwrap();
176
177 let result = system
180 .set_disposition(SIGQUIT, Disposition::Default)
181 .now_or_never();
182 assert_eq!(result, None);
183 assert_eq!(
184 inner.current_process().state(),
185 ProcessState::Halted(ProcessResult::Signaled {
186 signal: SIGQUIT,
187 core_dump: true
188 })
189 );
190 }
191
192 #[test]
193 fn first_update_sigmask_and_select_mask_updates_blocking_mask() {
194 let inner = VirtualSystem::new();
195 _ = inner
196 .current_process_mut()
197 .block_signals(SigmaskOp::Set, &[SIGQUIT, SIGTERM, SIGUSR1]);
198 let system = Rc::new(Concurrent::new(inner.clone()));
199
200 let result = system
201 .update_sigmask_and_select_mask(SigmaskOp::Add, SIGTERM)
202 .now_or_never()
203 .unwrap();
204 assert_eq!(result, Ok(()));
205 let blocked_signals = inner
206 .current_process()
207 .blocked_signals()
208 .iter()
209 .copied()
210 .collect::<Vec<_>>();
211 assert_eq!(blocked_signals, [SIGQUIT, SIGTERM, SIGUSR1]);
212 }
213
214 #[test]
215 fn first_update_sigmask_and_select_mask_sets_select_mask() {
216 let inner = VirtualSystem::new();
217 _ = inner
218 .current_process_mut()
219 .block_signals(SigmaskOp::Set, &[SIGQUIT, SIGTERM, SIGUSR1]);
220 let system = Rc::new(Concurrent::new(inner.clone()));
221
222 system
223 .update_sigmask_and_select_mask(SigmaskOp::Add, SIGTERM)
224 .now_or_never()
225 .unwrap()
226 .unwrap();
227 assert_eq!(
228 system.state.borrow().select_mask.as_deref(),
229 Some([SIGQUIT, SIGUSR1].as_slice())
230 );
231 }
232
233 #[ignore = "current VirtualSystem::sigmask silently ignores invalid signals"]
234 #[test]
235 fn first_update_sigmask_and_select_mask_leaves_select_mask_unchanged_on_error() {
236 let system = Rc::new(Concurrent::new(VirtualSystem::new()));
237 let invalid_signal = Number::from_raw_unchecked(NonZero::new(-1).unwrap());
238 let result = system
239 .update_sigmask_and_select_mask(SigmaskOp::Add, invalid_signal)
240 .now_or_never()
241 .unwrap();
242 assert_eq!(result, Err(Errno::EINVAL));
243 assert_eq!(system.state.borrow().select_mask.as_deref(), None);
244 }
245
246 #[test]
247 fn second_update_sigmask_and_select_mask_updates_select_mask() {
248 let inner = VirtualSystem::new();
249 _ = inner
250 .current_process_mut()
251 .block_signals(SigmaskOp::Set, &[SIGQUIT, SIGTERM, SIGUSR1]);
252 let system = Rc::new(Concurrent::new(inner.clone()));
253
254 system
255 .update_sigmask_and_select_mask(SigmaskOp::Add, SIGTERM)
256 .now_or_never()
257 .unwrap()
258 .unwrap();
259 system
260 .update_sigmask_and_select_mask(SigmaskOp::Remove, SIGQUIT)
261 .now_or_never()
262 .unwrap()
263 .unwrap();
264 assert_eq!(
265 system.state.borrow().select_mask.as_deref(),
266 Some([SIGUSR1].as_slice())
267 );
268 }
269}