use super::signal;
use super::ChildProcessStarter;
use super::Dir;
use super::Disposition;
use super::Errno;
use super::FdFlag;
use super::Gid;
use super::LimitPair;
use super::Mode;
use super::OfdAccess;
use super::OpenFlag;
use super::Path;
use super::PathBuf;
use super::Resource;
use super::Result;
use super::SelectSystem;
use super::SigmaskOp;
use super::SignalStatus;
use super::SignalSystem;
use super::Stat;
use super::System;
use super::SystemEx;
use super::Times;
use super::Uid;
use super::UnixString;
use crate::io::Fd;
use crate::job::Pid;
use crate::job::ProcessState;
#[cfg(doc)]
use crate::Env;
use enumset::EnumSet;
use std::cell::RefCell;
use std::convert::Infallible;
use std::ffi::c_int;
use std::ffi::CStr;
use std::ffi::CString;
use std::future::poll_fn;
use std::future::Future;
use std::io::SeekFrom;
use std::pin::Pin;
use std::rc::Rc;
use std::task::Poll;
use std::time::Duration;
use std::time::Instant;
#[derive(Clone, Debug)]
pub struct SharedSystem(pub(super) Rc<RefCell<SelectSystem>>);
impl SharedSystem {
pub fn new(system: Box<dyn System>) -> Self {
SharedSystem(Rc::new(RefCell::new(SelectSystem::new(system))))
}
pub async fn read_async(&self, fd: Fd, buffer: &mut [u8]) -> Result<usize> {
let was_nonblocking = (&mut &*self).get_and_set_nonblocking(fd, true)?;
let waker = Rc::new(RefCell::new(None));
let result = poll_fn(|context| {
let mut inner = self.0.borrow_mut();
match inner.read(fd, buffer) {
Err(Errno::EAGAIN) => {
*waker.borrow_mut() = Some(context.waker().clone());
inner.add_reader(fd, Rc::downgrade(&waker));
Poll::Pending
}
result => Poll::Ready(result),
}
})
.await;
_ = (&mut &*self).get_and_set_nonblocking(fd, was_nonblocking);
result
}
pub async fn write_all(&self, fd: Fd, mut buffer: &[u8]) -> Result<usize> {
if buffer.is_empty() {
return Ok(0);
}
let was_nonblocking = (&mut &*self).get_and_set_nonblocking(fd, true)?;
let mut written = 0;
let waker = Rc::new(RefCell::new(None));
let result = poll_fn(|context| {
let mut inner = self.0.borrow_mut();
match inner.write(fd, buffer) {
Ok(count) => {
written += count;
buffer = &buffer[count..];
if buffer.is_empty() {
return Poll::Ready(Ok(written));
}
}
Err(Errno::EAGAIN | Errno::EINTR) => (),
Err(error) => return Poll::Ready(Err(error)),
}
*waker.borrow_mut() = Some(context.waker().clone());
inner.add_writer(fd, Rc::downgrade(&waker));
Poll::Pending
})
.await;
_ = (&mut &*self).get_and_set_nonblocking(fd, was_nonblocking);
result
}
pub async fn print_error(&self, message: &str) {
_ = self.write_all(Fd::STDERR, message.as_bytes()).await;
}
pub async fn wait_until(&self, target: Instant) {
let waker = Rc::new(RefCell::new(None));
poll_fn(|context| {
let mut system = self.0.borrow_mut();
let now = system.now();
if now >= target {
return Poll::Ready(());
}
*waker.borrow_mut() = Some(context.waker().clone());
system.add_timeout(target, Rc::downgrade(&waker));
Poll::Pending
})
.await
}
pub async fn wait_for_signals(&self) -> Rc<[signal::Number]> {
let status = self.0.borrow_mut().add_signal_waker();
poll_fn(|context| {
let mut status = status.borrow_mut();
let dummy_status = SignalStatus::Expected(None);
let old_status = std::mem::replace(&mut *status, dummy_status);
match old_status {
SignalStatus::Caught(signals) => Poll::Ready(signals),
SignalStatus::Expected(_) => {
*status = SignalStatus::Expected(Some(context.waker().clone()));
Poll::Pending
}
}
})
.await
}
pub async fn wait_for_signal(&self, signal: signal::Number) {
while !self.wait_for_signals().await.contains(&signal) {}
}
pub fn select(&self, poll: bool) -> Result<()> {
self.0.borrow_mut().select(poll)
}
}
impl System for &SharedSystem {
fn fstat(&self, fd: Fd) -> Result<Stat> {
self.0.borrow().fstat(fd)
}
fn fstatat(&self, dir_fd: Fd, path: &CStr, follow_symlinks: bool) -> Result<Stat> {
self.0.borrow().fstatat(dir_fd, path, follow_symlinks)
}
fn is_executable_file(&self, path: &CStr) -> bool {
self.0.borrow().is_executable_file(path)
}
fn is_directory(&self, path: &CStr) -> bool {
self.0.borrow().is_directory(path)
}
fn pipe(&mut self) -> Result<(Fd, Fd)> {
self.0.borrow_mut().pipe()
}
fn dup(&mut self, from: Fd, to_min: Fd, flags: EnumSet<FdFlag>) -> Result<Fd> {
self.0.borrow_mut().dup(from, to_min, flags)
}
fn dup2(&mut self, from: Fd, to: Fd) -> Result<Fd> {
self.0.borrow_mut().dup2(from, to)
}
fn open(
&mut self,
path: &CStr,
access: OfdAccess,
flags: EnumSet<OpenFlag>,
mode: Mode,
) -> Result<Fd> {
self.0.borrow_mut().open(path, access, flags, mode)
}
fn open_tmpfile(&mut self, parent_dir: &Path) -> Result<Fd> {
self.0.borrow_mut().open_tmpfile(parent_dir)
}
fn close(&mut self, fd: Fd) -> Result<()> {
self.0.borrow_mut().close(fd)
}
fn ofd_access(&self, fd: Fd) -> Result<OfdAccess> {
self.0.borrow().ofd_access(fd)
}
fn get_and_set_nonblocking(&mut self, fd: Fd, nonblocking: bool) -> Result<bool> {
self.0.borrow_mut().get_and_set_nonblocking(fd, nonblocking)
}
fn fcntl_getfd(&self, fd: Fd) -> Result<EnumSet<FdFlag>> {
self.0.borrow().fcntl_getfd(fd)
}
fn fcntl_setfd(&mut self, fd: Fd, flags: EnumSet<FdFlag>) -> Result<()> {
self.0.borrow_mut().fcntl_setfd(fd, flags)
}
fn isatty(&self, fd: Fd) -> bool {
self.0.borrow().isatty(fd)
}
fn read(&mut self, fd: Fd, buffer: &mut [u8]) -> Result<usize> {
self.0.borrow_mut().read(fd, buffer)
}
fn write(&mut self, fd: Fd, buffer: &[u8]) -> Result<usize> {
self.0.borrow_mut().write(fd, buffer)
}
fn lseek(&mut self, fd: Fd, position: SeekFrom) -> Result<u64> {
self.0.borrow_mut().lseek(fd, position)
}
fn fdopendir(&mut self, fd: Fd) -> Result<Box<dyn Dir>> {
self.0.borrow_mut().fdopendir(fd)
}
fn opendir(&mut self, path: &CStr) -> Result<Box<dyn Dir>> {
self.0.borrow_mut().opendir(path)
}
fn umask(&mut self, mask: Mode) -> Mode {
self.0.borrow_mut().umask(mask)
}
fn now(&self) -> Instant {
self.0.borrow().now()
}
fn times(&self) -> Result<Times> {
self.0.borrow().times()
}
fn validate_signal(&self, number: signal::RawNumber) -> Option<(signal::Name, signal::Number)> {
self.0.borrow().validate_signal(number)
}
fn signal_number_from_name(&self, name: signal::Name) -> Option<signal::Number> {
self.0.borrow().signal_number_from_name(name)
}
fn sigmask(
&mut self,
op: Option<(SigmaskOp, &[signal::Number])>,
old_mask: Option<&mut Vec<signal::Number>>,
) -> Result<()> {
(**self.0.borrow_mut()).sigmask(op, old_mask)
}
fn sigaction(&mut self, signal: signal::Number, action: Disposition) -> Result<Disposition> {
self.0.borrow_mut().sigaction(signal, action)
}
fn caught_signals(&mut self) -> Vec<signal::Number> {
self.0.borrow_mut().caught_signals()
}
fn kill(
&mut self,
target: Pid,
signal: Option<signal::Number>,
) -> Pin<Box<(dyn Future<Output = Result<()>>)>> {
self.0.borrow_mut().kill(target, signal)
}
fn select(
&mut self,
readers: &mut Vec<Fd>,
writers: &mut Vec<Fd>,
timeout: Option<Duration>,
signal_mask: Option<&[signal::Number]>,
) -> Result<c_int> {
(**self.0.borrow_mut()).select(readers, writers, timeout, signal_mask)
}
fn getpid(&self) -> Pid {
self.0.borrow().getpid()
}
fn getppid(&self) -> Pid {
self.0.borrow().getppid()
}
fn getpgrp(&self) -> Pid {
self.0.borrow().getpgrp()
}
fn setpgid(&mut self, pid: Pid, pgid: Pid) -> Result<()> {
self.0.borrow_mut().setpgid(pid, pgid)
}
fn tcgetpgrp(&self, fd: Fd) -> Result<Pid> {
self.0.borrow().tcgetpgrp(fd)
}
fn tcsetpgrp(&mut self, fd: Fd, pgid: Pid) -> Result<()> {
self.0.borrow_mut().tcsetpgrp(fd, pgid)
}
fn new_child_process(&mut self) -> Result<ChildProcessStarter> {
self.0.borrow_mut().new_child_process()
}
fn wait(&mut self, target: Pid) -> Result<Option<(Pid, ProcessState)>> {
self.0.borrow_mut().wait(target)
}
fn execve(&mut self, path: &CStr, args: &[CString], envs: &[CString]) -> Result<Infallible> {
self.0.borrow_mut().execve(path, args, envs)
}
fn getcwd(&self) -> Result<PathBuf> {
self.0.borrow().getcwd()
}
fn chdir(&mut self, path: &CStr) -> Result<()> {
self.0.borrow_mut().chdir(path)
}
fn getuid(&self) -> Uid {
self.0.borrow().getuid()
}
fn geteuid(&self) -> Uid {
self.0.borrow().geteuid()
}
fn getgid(&self) -> Gid {
self.0.borrow().getgid()
}
fn getegid(&self) -> Gid {
self.0.borrow().getegid()
}
fn getpwnam_dir(&self, name: &str) -> Result<Option<PathBuf>> {
self.0.borrow().getpwnam_dir(name)
}
fn confstr_path(&self) -> Result<UnixString> {
self.0.borrow().confstr_path()
}
fn shell_path(&self) -> CString {
self.0.borrow().shell_path()
}
fn getrlimit(&self, resource: Resource) -> Result<LimitPair> {
self.0.borrow().getrlimit(resource)
}
fn setrlimit(&mut self, resource: Resource, limits: LimitPair) -> Result<()> {
self.0.borrow_mut().setrlimit(resource, limits)
}
}
impl System for SharedSystem {
#[inline]
fn fstat(&self, fd: Fd) -> Result<Stat> {
(&self).fstat(fd)
}
#[inline]
fn fstatat(&self, dir_fd: Fd, path: &CStr, follow_symlinks: bool) -> Result<Stat> {
(&self).fstatat(dir_fd, path, follow_symlinks)
}
#[inline]
fn is_executable_file(&self, path: &CStr) -> bool {
(&self).is_executable_file(path)
}
#[inline]
fn is_directory(&self, path: &CStr) -> bool {
(&self).is_directory(path)
}
#[inline]
fn pipe(&mut self) -> Result<(Fd, Fd)> {
(&mut &*self).pipe()
}
#[inline]
fn dup(&mut self, from: Fd, to_min: Fd, flags: EnumSet<FdFlag>) -> Result<Fd> {
(&mut &*self).dup(from, to_min, flags)
}
#[inline]
fn dup2(&mut self, from: Fd, to: Fd) -> Result<Fd> {
(&mut &*self).dup2(from, to)
}
#[inline]
fn open(
&mut self,
path: &CStr,
access: OfdAccess,
flags: EnumSet<OpenFlag>,
mode: Mode,
) -> Result<Fd> {
(&mut &*self).open(path, access, flags, mode)
}
#[inline]
fn open_tmpfile(&mut self, parent_dir: &Path) -> Result<Fd> {
(&mut &*self).open_tmpfile(parent_dir)
}
#[inline]
fn close(&mut self, fd: Fd) -> Result<()> {
(&mut &*self).close(fd)
}
#[inline]
fn ofd_access(&self, fd: Fd) -> Result<OfdAccess> {
(&self).ofd_access(fd)
}
#[inline]
fn get_and_set_nonblocking(&mut self, fd: Fd, nonblocking: bool) -> Result<bool> {
(&mut &*self).get_and_set_nonblocking(fd, nonblocking)
}
#[inline]
fn fcntl_getfd(&self, fd: Fd) -> Result<EnumSet<FdFlag>> {
(&self).fcntl_getfd(fd)
}
#[inline]
fn fcntl_setfd(&mut self, fd: Fd, flags: EnumSet<FdFlag>) -> Result<()> {
(&mut &*self).fcntl_setfd(fd, flags)
}
#[inline]
fn isatty(&self, fd: Fd) -> bool {
(&self).isatty(fd)
}
#[inline]
fn read(&mut self, fd: Fd, buffer: &mut [u8]) -> Result<usize> {
(&mut &*self).read(fd, buffer)
}
#[inline]
fn write(&mut self, fd: Fd, buffer: &[u8]) -> Result<usize> {
(&mut &*self).write(fd, buffer)
}
#[inline]
fn lseek(&mut self, fd: Fd, position: SeekFrom) -> Result<u64> {
(&mut &*self).lseek(fd, position)
}
#[inline]
fn fdopendir(&mut self, fd: Fd) -> Result<Box<dyn Dir>> {
(&mut &*self).fdopendir(fd)
}
#[inline]
fn opendir(&mut self, path: &CStr) -> Result<Box<dyn Dir>> {
(&mut &*self).opendir(path)
}
#[inline]
fn umask(&mut self, mask: Mode) -> Mode {
(&mut &*self).umask(mask)
}
#[inline]
fn now(&self) -> Instant {
(&self).now()
}
#[inline]
fn times(&self) -> Result<Times> {
(&self).times()
}
#[inline]
fn validate_signal(&self, number: signal::RawNumber) -> Option<(signal::Name, signal::Number)> {
(&self).validate_signal(number)
}
#[inline]
fn signal_number_from_name(&self, name: signal::Name) -> Option<signal::Number> {
System::signal_number_from_name(&self, name)
}
#[inline]
fn sigmask(
&mut self,
op: Option<(SigmaskOp, &[signal::Number])>,
old_mask: Option<&mut Vec<signal::Number>>,
) -> Result<()> {
(&mut &*self).sigmask(op, old_mask)
}
#[inline]
fn sigaction(&mut self, signal: signal::Number, action: Disposition) -> Result<Disposition> {
(&mut &*self).sigaction(signal, action)
}
#[inline]
fn caught_signals(&mut self) -> Vec<signal::Number> {
(&mut &*self).caught_signals()
}
#[inline]
fn kill(
&mut self,
target: Pid,
signal: Option<signal::Number>,
) -> Pin<Box<dyn Future<Output = Result<()>>>> {
(&mut &*self).kill(target, signal)
}
#[inline]
fn select(
&mut self,
readers: &mut Vec<Fd>,
writers: &mut Vec<Fd>,
timeout: Option<Duration>,
signal_mask: Option<&[signal::Number]>,
) -> Result<c_int> {
(&mut &*self).select(readers, writers, timeout, signal_mask)
}
#[inline]
fn getpid(&self) -> Pid {
(&self).getpid()
}
#[inline]
fn getppid(&self) -> Pid {
(&self).getppid()
}
#[inline]
fn getpgrp(&self) -> Pid {
(&self).getpgrp()
}
#[inline]
fn setpgid(&mut self, pid: Pid, pgid: Pid) -> Result<()> {
(&mut &*self).setpgid(pid, pgid)
}
#[inline]
fn tcgetpgrp(&self, fd: Fd) -> Result<Pid> {
(&self).tcgetpgrp(fd)
}
#[inline]
fn tcsetpgrp(&mut self, fd: Fd, pgid: Pid) -> Result<()> {
(&mut &*self).tcsetpgrp(fd, pgid)
}
#[inline]
fn new_child_process(&mut self) -> Result<ChildProcessStarter> {
(&mut &*self).new_child_process()
}
#[inline]
fn wait(&mut self, target: Pid) -> Result<Option<(Pid, ProcessState)>> {
(&mut &*self).wait(target)
}
#[inline]
fn execve(&mut self, path: &CStr, args: &[CString], envs: &[CString]) -> Result<Infallible> {
(&mut &*self).execve(path, args, envs)
}
#[inline]
fn getcwd(&self) -> Result<PathBuf> {
(&self).getcwd()
}
#[inline]
fn chdir(&mut self, path: &CStr) -> Result<()> {
(&mut &*self).chdir(path)
}
#[inline]
fn getuid(&self) -> Uid {
(&self).getuid()
}
#[inline]
fn geteuid(&self) -> Uid {
(&self).geteuid()
}
#[inline]
fn getgid(&self) -> Gid {
(&self).getgid()
}
#[inline]
fn getegid(&self) -> Gid {
(&self).getegid()
}
#[inline]
fn getpwnam_dir(&self, name: &str) -> Result<Option<PathBuf>> {
(&self).getpwnam_dir(name)
}
#[inline]
fn confstr_path(&self) -> Result<UnixString> {
(&self).confstr_path()
}
#[inline]
fn shell_path(&self) -> CString {
(&self).shell_path()
}
#[inline]
fn getrlimit(&self, resource: Resource) -> Result<LimitPair> {
(&self).getrlimit(resource)
}
#[inline]
fn setrlimit(&mut self, resource: Resource, limits: LimitPair) -> Result<()> {
(&mut &*self).setrlimit(resource, limits)
}
}
impl SignalSystem for &SharedSystem {
#[inline]
fn signal_name_from_number(&self, number: signal::Number) -> signal::Name {
SystemEx::signal_name_from_number(*self, number)
}
#[inline]
fn signal_number_from_name(&self, name: signal::Name) -> Option<signal::Number> {
System::signal_number_from_name(*self, name)
}
fn set_disposition(
&mut self,
signal: signal::Number,
disposition: Disposition,
) -> Result<Disposition> {
self.0.borrow_mut().set_disposition(signal, disposition)
}
}
impl SignalSystem for SharedSystem {
#[inline]
fn signal_name_from_number(&self, number: signal::Number) -> signal::Name {
SystemEx::signal_name_from_number(self, number)
}
#[inline]
fn signal_number_from_name(&self, name: signal::Name) -> Option<signal::Number> {
System::signal_number_from_name(self, name)
}
#[inline]
fn set_disposition(
&mut self,
signal: signal::Number,
disposition: Disposition,
) -> Result<Disposition> {
self.0.borrow_mut().set_disposition(signal, disposition)
}
}
#[cfg(test)]
mod tests {
use super::super::r#virtual::VirtualSystem;
use super::super::r#virtual::PIPE_SIZE;
use super::super::r#virtual::{SIGCHLD, SIGINT, SIGTERM, SIGUSR1};
use super::*;
use assert_matches::assert_matches;
use futures_util::task::noop_waker_ref;
use futures_util::FutureExt as _;
use std::task::Context;
use std::task::Poll;
use std::time::Duration;
#[test]
fn shared_system_read_async_ready() {
let mut system = SharedSystem::new(Box::new(VirtualSystem::new()));
let (reader, writer) = system.pipe().unwrap();
system.write(writer, &[42]).unwrap();
let mut buffer = [0; 2];
let result = system.read_async(reader, &mut buffer).now_or_never();
assert_eq!(result, Some(Ok(1)));
assert_eq!(buffer[..1], [42]);
}
#[test]
fn shared_system_read_async_not_ready_at_first() {
let system = VirtualSystem::new();
let process_id = system.process_id;
let state = Rc::clone(&system.state);
let mut system = SharedSystem::new(Box::new(system));
let system2 = system.clone();
let (reader, writer) = system.pipe().unwrap();
let mut context = Context::from_waker(noop_waker_ref());
let mut buffer = [0; 2];
let mut future = Box::pin(system.read_async(reader, &mut buffer));
let result = future.as_mut().poll(&mut context);
assert_eq!(result, Poll::Pending);
let result = system2.select(false);
assert_eq!(result, Ok(()));
let result = future.as_mut().poll(&mut context);
assert_eq!(result, Poll::Pending);
state.borrow_mut().processes[&process_id].fds[&writer]
.open_file_description
.borrow_mut()
.write(&[56])
.unwrap();
let result = future.as_mut().poll(&mut context);
drop(future);
assert_eq!(result, Poll::Ready(Ok(1)));
assert_eq!(buffer[..1], [56]);
}
#[test]
fn shared_system_write_all_ready() {
let mut system = SharedSystem::new(Box::new(VirtualSystem::new()));
let (reader, writer) = system.pipe().unwrap();
let result = system.write_all(writer, &[17]).now_or_never().unwrap();
assert_eq!(result, Ok(1));
let mut buffer = [0; 2];
system.read(reader, &mut buffer).unwrap();
assert_eq!(buffer[..1], [17]);
}
#[test]
fn shared_system_write_all_not_ready_at_first() {
let system = VirtualSystem::new();
let process_id = system.process_id;
let state = Rc::clone(&system.state);
let mut system = SharedSystem::new(Box::new(system));
let (reader, writer) = system.pipe().unwrap();
state.borrow_mut().processes[&process_id].fds[&writer]
.open_file_description
.borrow_mut()
.write(&[42; PIPE_SIZE])
.unwrap();
let mut context = Context::from_waker(noop_waker_ref());
let mut out_buffer = [87; PIPE_SIZE];
out_buffer[0] = 0;
out_buffer[1] = 1;
out_buffer[PIPE_SIZE - 2] = 0xFE;
out_buffer[PIPE_SIZE - 1] = 0xFF;
let mut future = Box::pin(system.write_all(writer, &out_buffer));
let result = future.as_mut().poll(&mut context);
assert_eq!(result, Poll::Pending);
let mut in_buffer = [0; PIPE_SIZE - 1];
state.borrow_mut().processes[&process_id].fds[&reader]
.open_file_description
.borrow_mut()
.read(&mut in_buffer)
.unwrap();
assert_eq!(in_buffer, [42; PIPE_SIZE - 1]);
let result = future.as_mut().poll(&mut context);
assert_eq!(result, Poll::Pending);
in_buffer[0] = 0;
state.borrow_mut().processes[&process_id].fds[&reader]
.open_file_description
.borrow_mut()
.read(&mut in_buffer[..1])
.unwrap();
assert_eq!(in_buffer[..1], [42; 1]);
let result = future.as_mut().poll(&mut context);
assert_eq!(result, Poll::Ready(Ok(out_buffer.len())));
state.borrow_mut().processes[&process_id].fds[&reader]
.open_file_description
.borrow_mut()
.read(&mut in_buffer)
.unwrap();
assert_eq!(in_buffer, out_buffer[..PIPE_SIZE - 1]);
state.borrow_mut().processes[&process_id].fds[&reader]
.open_file_description
.borrow_mut()
.read(&mut in_buffer)
.unwrap();
assert_eq!(in_buffer[..1], out_buffer[PIPE_SIZE - 1..]);
}
#[test]
fn shared_system_write_all_empty() {
let system = VirtualSystem::new();
let process_id = system.process_id;
let state = Rc::clone(&system.state);
let mut system = SharedSystem::new(Box::new(system));
let (_reader, writer) = system.pipe().unwrap();
state.borrow_mut().processes[&process_id].fds[&writer]
.open_file_description
.borrow_mut()
.write(&[0; PIPE_SIZE])
.unwrap();
let mut context = Context::from_waker(noop_waker_ref());
let mut future = Box::pin(system.write_all(writer, &[]));
let result = future.as_mut().poll(&mut context);
assert_eq!(result, Poll::Ready(Ok(0)));
}
#[test]
fn shared_system_wait_until() {
let system = VirtualSystem::new();
let state = Rc::clone(&system.state);
let system = SharedSystem::new(Box::new(system));
let start = Instant::now();
state.borrow_mut().now = Some(start);
let target = start + Duration::from_millis(1_125);
let mut future = Box::pin(system.wait_until(target));
let mut context = Context::from_waker(noop_waker_ref());
let poll = future.as_mut().poll(&mut context);
assert_eq!(poll, Poll::Pending);
system.select(false).unwrap();
let poll = future.as_mut().poll(&mut context);
assert_eq!(poll, Poll::Ready(()));
assert_eq!(state.borrow().now, Some(target));
}
#[test]
fn shared_system_wait_for_signals() {
let system = VirtualSystem::new();
let process_id = system.process_id;
let state = Rc::clone(&system.state);
let mut system = SharedSystem::new(Box::new(system));
system.set_disposition(SIGCHLD, Disposition::Catch).unwrap();
system.set_disposition(SIGINT, Disposition::Catch).unwrap();
system.set_disposition(SIGUSR1, Disposition::Catch).unwrap();
let mut context = Context::from_waker(noop_waker_ref());
let mut future = Box::pin(system.wait_for_signals());
let result = future.as_mut().poll(&mut context);
assert_eq!(result, Poll::Pending);
{
let mut state = state.borrow_mut();
let process = state.processes.get_mut(&process_id).unwrap();
assert!(process.blocked_signals().contains(&SIGCHLD));
assert!(process.blocked_signals().contains(&SIGINT));
assert!(process.blocked_signals().contains(&SIGUSR1));
let _ = process.raise_signal(SIGCHLD);
let _ = process.raise_signal(SIGINT);
}
let result = future.as_mut().poll(&mut context);
assert_eq!(result, Poll::Pending);
system.select(false).unwrap();
let result = future.as_mut().poll(&mut context);
assert_matches!(result, Poll::Ready(signals) => {
assert_eq!(signals.len(), 2);
assert!(signals.contains(&SIGCHLD));
assert!(signals.contains(&SIGINT));
});
}
#[test]
fn shared_system_wait_for_signal_returns_on_caught() {
let system = VirtualSystem::new();
let process_id = system.process_id;
let state = Rc::clone(&system.state);
let mut system = SharedSystem::new(Box::new(system));
system.set_disposition(SIGCHLD, Disposition::Catch).unwrap();
let mut context = Context::from_waker(noop_waker_ref());
let mut future = Box::pin(system.wait_for_signal(SIGCHLD));
let result = future.as_mut().poll(&mut context);
assert_eq!(result, Poll::Pending);
{
let mut state = state.borrow_mut();
let process = state.processes.get_mut(&process_id).unwrap();
assert!(process.blocked_signals().contains(&SIGCHLD));
let _ = process.raise_signal(SIGCHLD);
}
let result = future.as_mut().poll(&mut context);
assert_eq!(result, Poll::Pending);
system.select(false).unwrap();
let result = future.as_mut().poll(&mut context);
assert_eq!(result, Poll::Ready(()));
}
#[test]
fn shared_system_wait_for_signal_ignores_irrelevant_signals() {
let system = VirtualSystem::new();
let process_id = system.process_id;
let state = Rc::clone(&system.state);
let mut system = SharedSystem::new(Box::new(system));
system.set_disposition(SIGINT, Disposition::Catch).unwrap();
system.set_disposition(SIGTERM, Disposition::Catch).unwrap();
let mut context = Context::from_waker(noop_waker_ref());
let mut future = Box::pin(system.wait_for_signal(SIGINT));
let result = future.as_mut().poll(&mut context);
assert_eq!(result, Poll::Pending);
{
let mut state = state.borrow_mut();
let process = state.processes.get_mut(&process_id).unwrap();
let _ = process.raise_signal(SIGCHLD);
let _ = process.raise_signal(SIGTERM);
}
system.select(false).unwrap();
let result = future.as_mut().poll(&mut context);
assert_eq!(result, Poll::Pending);
}
#[test]
fn shared_system_select_consumes_all_pending_signals() {
let system = VirtualSystem::new();
let process_id = system.process_id;
let state = Rc::clone(&system.state);
let mut system = SharedSystem::new(Box::new(system));
system.set_disposition(SIGINT, Disposition::Catch).unwrap();
system.set_disposition(SIGTERM, Disposition::Catch).unwrap();
{
let mut state = state.borrow_mut();
let process = state.processes.get_mut(&process_id).unwrap();
let _ = process.raise_signal(SIGINT);
let _ = process.raise_signal(SIGTERM);
}
system.select(false).unwrap();
let state = state.borrow();
let process = state.processes.get(&process_id).unwrap();
let blocked = process.blocked_signals();
assert!(blocked.contains(&SIGINT));
assert!(blocked.contains(&SIGTERM));
let pending = process.pending_signals();
assert!(!pending.contains(&SIGINT));
assert!(!pending.contains(&SIGTERM));
}
#[test]
fn shared_system_select_does_not_wake_signal_waiters_on_io() {
let system = VirtualSystem::new();
let mut system_1 = SharedSystem::new(Box::new(system));
let mut system_2 = system_1.clone();
let mut system_3 = system_1.clone();
let (reader, writer) = system_1.pipe().unwrap();
system_2
.set_disposition(SIGCHLD, Disposition::Catch)
.unwrap();
let mut buffer = [0];
let mut read_future = Box::pin(system_1.read_async(reader, &mut buffer));
let mut signal_future = Box::pin(system_2.wait_for_signals());
let mut context = Context::from_waker(noop_waker_ref());
let result = read_future.as_mut().poll(&mut context);
assert_eq!(result, Poll::Pending);
let result = signal_future.as_mut().poll(&mut context);
assert_eq!(result, Poll::Pending);
system_3.write(writer, &[42]).unwrap();
system_3.select(false).unwrap();
let result = read_future.as_mut().poll(&mut context);
assert_eq!(result, Poll::Ready(Ok(1)));
let result = signal_future.as_mut().poll(&mut context);
assert_eq!(result, Poll::Pending);
}
#[test]
fn shared_system_select_poll() {
let system = VirtualSystem::new();
let state = Rc::clone(&system.state);
let system = SharedSystem::new(Box::new(system));
let start = Instant::now();
state.borrow_mut().now = Some(start);
let target = start + Duration::from_millis(1_125);
let mut future = Box::pin(system.wait_until(target));
let mut context = Context::from_waker(noop_waker_ref());
let poll = future.as_mut().poll(&mut context);
assert_eq!(poll, Poll::Pending);
system.select(true).unwrap();
let poll = future.as_mut().poll(&mut context);
assert_eq!(poll, Poll::Pending);
assert_eq!(state.borrow().now, Some(start));
}
}