yash_env/system/concurrency/
signal.rs1use super::Concurrent;
20use crate::job::{RunBlocking, RunUnblocking};
21use crate::signal::Number;
22use crate::subshell::BlockSignals;
23use crate::system::signal::Sigset as _;
24use crate::system::{Disposition, Errno, Sigaction, Sigmask, SigmaskOp};
25use crate::trap::SignalSystem;
26use std::rc::Rc;
27
28impl<S> SignalSystem for Rc<Concurrent<S>>
33where
34 S: Sigmask + Sigaction,
35{
36 fn get_disposition(&self, signal: Number) -> Result<Disposition, Errno> {
42 self.inner.get_sigaction(signal)
43 }
44
45 fn set_disposition(
57 &self,
58 signal: Number,
59 disposition: Disposition,
60 ) -> impl Future<Output = Result<Disposition, Errno>> + use<S> {
61 let this = Rc::clone(self);
62 async move {
63 if disposition == Disposition::Catch {
64 this.update_sigmask_and_select_mask(SigmaskOp::Add, signal)
67 .await?;
68 }
69
70 let old_action = this.inner.sigaction(signal, disposition)?;
71
72 if disposition != Disposition::Catch {
73 this.update_sigmask_and_select_mask(SigmaskOp::Remove, signal)
76 .await?;
77 }
78
79 Ok(old_action)
80 }
81 }
82}
83
84impl<S> Concurrent<S>
85where
86 S: Sigmask,
87{
88 async fn update_sigmask_and_select_mask(
91 &self,
92 op: SigmaskOp,
93 signal: Number,
94 ) -> Result<(), Errno> {
95 let mut mask_of_signal = S::Sigset::new();
96 mask_of_signal.insert(signal)?;
97 let mut old_mask = S::Sigset::new();
98 self.inner
99 .sigmask(Some((op, &mask_of_signal)), Some(&mut old_mask))
100 .await?;
101
102 self.state
103 .borrow_mut()
104 .select_mask
105 .get_or_insert(old_mask)
106 .remove(signal)
107 }
108}
109
110impl<S> BlockSignals for Concurrent<S>
111where
112 S: Sigmask + BlockSignals,
113{
114 type SavedMask = S::SavedMask;
115
116 async fn block_sigint_sigquit(&self) -> Result<Self::SavedMask, Errno> {
117 self.inner.block_sigint_sigquit().await
118 }
119
120 async fn restore_sigmask(&self, mask: Self::SavedMask) -> Result<(), Errno> {
121 self.inner.restore_sigmask(mask).await
122 }
123}
124
125impl<S> BlockSignals for Rc<Concurrent<S>>
126where
127 S: Sigmask + BlockSignals,
128{
129 type SavedMask = S::SavedMask;
130
131 async fn block_sigint_sigquit(&self) -> Result<Self::SavedMask, Errno> {
142 (self as &Concurrent<S>).block_sigint_sigquit().await
143 }
144
145 async fn restore_sigmask(&self, mask: Self::SavedMask) -> Result<(), Errno> {
146 (self as &Concurrent<S>).restore_sigmask(mask).await
147 }
148}
149
150impl<S> RunBlocking for Concurrent<S>
151where
152 S: Sigmask + RunBlocking,
153{
154 async fn run_blocking<F, T>(&self, signal: Number, f: F) -> Result<T, Errno>
155 where
156 F: AsyncFnOnce() -> Result<T, Errno>,
157 {
158 self.inner.run_blocking(signal, f).await
159 }
160}
161
162impl<S> RunBlocking for Rc<Concurrent<S>>
163where
164 S: Sigmask + RunBlocking,
165{
166 async fn run_blocking<F, T>(&self, signal: Number, f: F) -> Result<T, Errno>
174 where
175 F: AsyncFnOnce() -> Result<T, Errno>,
176 {
177 (self as &Concurrent<S>).run_blocking(signal, f).await
178 }
179}
180
181impl<S> RunUnblocking for Concurrent<S>
182where
183 S: Sigmask + RunUnblocking,
184{
185 async fn run_unblocking<F, T>(&self, signal: Number, f: F) -> Result<T, Errno>
186 where
187 F: AsyncFnOnce() -> Result<T, Errno>,
188 {
189 self.inner.run_unblocking(signal, f).await
190 }
191}
192
193impl<S> RunUnblocking for Rc<Concurrent<S>>
194where
195 S: Sigmask + RunUnblocking,
196{
197 async fn run_unblocking<F, T>(&self, signal: Number, f: F) -> Result<T, Errno>
205 where
206 F: AsyncFnOnce() -> Result<T, Errno>,
207 {
208 (self as &Concurrent<S>).run_unblocking(signal, f).await
209 }
210}
211
212#[cfg(test)]
213mod tests {
214 use super::*;
215 use crate::job::{ProcessResult, ProcessState};
216 use crate::system::SendSignal as _;
217 use crate::system::r#virtual::sigset::Sigset;
218 use crate::system::r#virtual::{SIGQUIT, SIGTERM, SIGUSR1, VirtualSystem};
219 use futures_util::FutureExt as _;
220 use std::num::NonZero;
221
222 #[test]
223 fn setting_disposition_from_default_to_catch() {
224 let inner = VirtualSystem::new();
225 let system = Rc::new(Concurrent::new(inner.clone()));
226 let result = system
227 .set_disposition(SIGTERM, Disposition::Catch)
228 .now_or_never()
229 .unwrap();
230 assert_eq!(result, Ok(Disposition::Default));
231 assert_eq!(system.get_disposition(SIGTERM), Ok(Disposition::Catch));
232
233 inner.raise(SIGTERM).now_or_never().unwrap().unwrap();
235 assert_eq!(inner.current_process().state(), ProcessState::Running);
237 }
238
239 #[test]
240 fn setting_disposition_from_default_to_ignore() {
241 let inner = VirtualSystem::new();
242 let system = Rc::new(Concurrent::new(inner.clone()));
243 let result = system
244 .set_disposition(SIGTERM, Disposition::Ignore)
245 .now_or_never()
246 .unwrap();
247 assert_eq!(result, Ok(Disposition::Default));
248 assert_eq!(system.get_disposition(SIGTERM), Ok(Disposition::Ignore));
249
250 inner.raise(SIGTERM).now_or_never().unwrap().unwrap();
252 assert_eq!(inner.current_process().state(), ProcessState::Running);
253 }
254
255 #[test]
256 fn setting_disposition_from_ignore_to_catch() {
257 let system = Rc::new(Concurrent::new(VirtualSystem::new()));
258 system
259 .set_disposition(SIGQUIT, Disposition::Ignore)
260 .now_or_never()
261 .unwrap()
262 .unwrap();
263
264 let result = system
265 .set_disposition(SIGQUIT, Disposition::Catch)
266 .now_or_never()
267 .unwrap();
268 assert_eq!(result, Ok(Disposition::Ignore));
269 assert_eq!(system.get_disposition(SIGQUIT), Ok(Disposition::Catch));
270 }
271
272 #[test]
273 fn setting_disposition_from_catch_to_default() {
274 let inner = VirtualSystem::new();
275 let system = Rc::new(Concurrent::new(inner.clone()));
276 system
277 .set_disposition(SIGQUIT, Disposition::Catch)
278 .now_or_never()
279 .unwrap()
280 .unwrap();
281 system.raise(SIGQUIT).now_or_never().unwrap().unwrap();
283
284 let result = system
287 .set_disposition(SIGQUIT, Disposition::Default)
288 .now_or_never();
289 assert_eq!(result, None);
290 assert_eq!(
291 inner.current_process().state(),
292 ProcessState::Halted(ProcessResult::Signaled {
293 signal: SIGQUIT,
294 core_dump: true
295 })
296 );
297 }
298
299 #[test]
300 fn first_update_sigmask_and_select_mask_updates_blocking_mask() {
301 let inner = VirtualSystem::new();
302 _ = inner
303 .current_process_mut()
304 .block_signals(SigmaskOp::Set, [SIGQUIT, SIGTERM, SIGUSR1]);
305 let system = Rc::new(Concurrent::new(inner.clone()));
306
307 let result = system
308 .update_sigmask_and_select_mask(SigmaskOp::Add, SIGTERM)
309 .now_or_never()
310 .unwrap();
311 assert_eq!(result, Ok(()));
312 let blocked_signals = inner
313 .current_process()
314 .blocked_signals()
315 .iter()
316 .copied()
317 .collect::<Vec<_>>();
318 assert_eq!(blocked_signals, [SIGQUIT, SIGTERM, SIGUSR1]);
319 }
320
321 #[test]
322 fn first_update_sigmask_and_select_mask_sets_select_mask() {
323 let inner = VirtualSystem::new();
324 _ = inner
325 .current_process_mut()
326 .block_signals(SigmaskOp::Set, [SIGQUIT, SIGTERM, SIGUSR1]);
327 let system = Rc::new(Concurrent::new(inner.clone()));
328
329 system
330 .update_sigmask_and_select_mask(SigmaskOp::Add, SIGTERM)
331 .now_or_never()
332 .unwrap()
333 .unwrap();
334 assert_eq!(
335 system.state.borrow().select_mask,
336 Some(Sigset::from_iter([SIGQUIT, SIGUSR1]))
337 );
338 }
339
340 #[ignore = "current VirtualSystem::sigmask silently ignores invalid signals"]
341 #[test]
342 fn first_update_sigmask_and_select_mask_leaves_select_mask_unchanged_on_error() {
343 let system = Rc::new(Concurrent::new(VirtualSystem::new()));
344 let invalid_signal = Number::from_raw_unchecked(NonZero::new(-1).unwrap());
345 let result = system
346 .update_sigmask_and_select_mask(SigmaskOp::Add, invalid_signal)
347 .now_or_never()
348 .unwrap();
349 assert_eq!(result, Err(Errno::EINVAL));
350 assert_eq!(system.state.borrow().select_mask, None);
351 }
352
353 #[test]
354 fn second_update_sigmask_and_select_mask_updates_select_mask() {
355 let inner = VirtualSystem::new();
356 _ = inner
357 .current_process_mut()
358 .block_signals(SigmaskOp::Set, [SIGQUIT, SIGTERM, SIGUSR1]);
359 let system = Rc::new(Concurrent::new(inner.clone()));
360
361 system
362 .update_sigmask_and_select_mask(SigmaskOp::Add, SIGTERM)
363 .now_or_never()
364 .unwrap()
365 .unwrap();
366 system
367 .update_sigmask_and_select_mask(SigmaskOp::Remove, SIGQUIT)
368 .now_or_never()
369 .unwrap()
370 .unwrap();
371 assert_eq!(
372 system.state.borrow().select_mask,
373 Some(Sigset::from(SIGUSR1))
374 );
375 }
376}