use std::collections::{VecDeque, HashMap};
use std::cell::Cell;
#[cfg(unix)]
use super::utils::set_close_on_exec;
use super::exit_status::ExitStatus;
#[cfg(windows)]
struct SubprocessOs {
pub child: ::winapi::HANDLE,
pub pipe: ::winapi::HANDLE,
pub overlapped: ::winapi::OVERLAPPED,
pub overlapped_buf: [u8; 4096],
pub is_reading: bool,
}
#[cfg(windows)]
impl Default for SubprocessOs {
fn default() -> Self {
unsafe { ::std::mem::zeroed() }
}
}
#[cfg(unix)]
#[derive(Default)]
struct SubprocessOs {
pub fd: Option<::libc::c_int>,
pub pid: Option<::libc::pid_t>,
}
pub struct Subprocess {
use_console: bool,
buf: Vec<u8>,
extra: Box<SubprocessOs>,
}
impl Subprocess {
pub(super) fn new(use_console: bool) -> Box<Self> {
Box::new(Subprocess {
use_console,
buf: Vec::new(),
extra: Default::default(),
})
}
pub fn output(&self) -> &[u8] {
&self.buf
}
}
#[cfg(windows)]
impl Drop for Subprocess {
fn drop(&mut self) {
use winapi;
use errno;
use kernel32;
if !self.extra.pipe.is_null() {
if unsafe { kernel32::CloseHandle(self.extra.pipe) } == winapi::FALSE {
fatal!("CloseHandle: {}", errno::errno());
}
}
if self.exist() {
self.finish();
}
}
}
#[cfg(windows)]
impl Subprocess {
pub(super) fn exist(&self) -> bool {
!self.extra.child.is_null()
}
pub(super) fn start<T>(&mut self, set: &mut SubprocessSet<T>, command: &[u8]) -> bool {
use winapi;
use kernel32;
use std::ptr::null_mut;
use std::mem::{zeroed, size_of};
let child_pipe = self.setup_pipe(set.extra.ioport());
let mut security_attributes = unsafe { zeroed::<winapi::SECURITY_ATTRIBUTES>() };
security_attributes.nLength = size_of::<winapi::SECURITY_ATTRIBUTES>() as _;
security_attributes.bInheritHandle = winapi::TRUE;
let nul_name = wstrz!("NUL");
let nul = unsafe {
kernel32::CreateFileW(
nul_name.as_ptr(),
winapi::GENERIC_READ,
winapi::FILE_SHARE_READ | winapi::FILE_SHARE_WRITE | winapi::FILE_SHARE_DELETE,
&mut security_attributes as _,
winapi::OPEN_EXISTING,
0,
null_mut(),
)
};
if nul == winapi::INVALID_HANDLE_VALUE {
fatal!("couldn't open nul");
}
let mut startup_info = unsafe { zeroed::<winapi::STARTUPINFOW>() };
startup_info.cb = size_of::<winapi::STARTUPINFOW>() as _;
if !self.use_console {
startup_info.dwFlags = winapi::STARTF_USESTDHANDLES;
startup_info.hStdInput = nul;
startup_info.hStdOutput = child_pipe;
startup_info.hStdError = child_pipe;
}
let mut process_info = unsafe { zeroed::<winapi::PROCESS_INFORMATION>() };
let process_flags = if self.use_console {
0
} else {
winapi::CREATE_NEW_PROCESS_GROUP
};
let cmd_unicode = ::std::str::from_utf8(command);
let create_process_result = match &cmd_unicode {
&Err(_) => Err(None),
&Ok(ref cmd_unicode) => {
if let Ok(cmd) = ::widestring::WideCString::from_str(cmd_unicode) {
let mut cmd = cmd.into_vec();
if unsafe {
kernel32::CreateProcessW(
null_mut(),
cmd.as_mut_ptr(),
null_mut(),
null_mut(),
winapi::TRUE,
process_flags,
null_mut(),
null_mut(),
&mut startup_info as _,
&mut process_info as _,
)
} != winapi::FALSE
{
Ok(())
} else {
Err(Some(unsafe { kernel32::GetLastError() }))
}
} else {
Err(None)
}
}
};
if !child_pipe.is_null() {
unsafe { kernel32::CloseHandle(child_pipe) };
}
unsafe { kernel32::CloseHandle(nul) };
match create_process_result {
Ok(()) => {
unsafe { kernel32::CloseHandle(process_info.hThread) };
self.extra.child = process_info.hProcess;
true
}
Err(e @ Some(winapi::ERROR_FILE_NOT_FOUND)) |
Err(e @ None) => {
unsafe { kernel32::CloseHandle(self.extra.pipe) };
self.extra.pipe = null_mut();
self.buf = if e.is_some() {
b"CreateProcess failed: The system cannot find the file specified.\n"
.as_ref()
.to_owned()
} else {
b"CreateProcess failed: The command is not valid UTF-8 string.\n"
.as_ref()
.to_owned()
};
true
}
Err(Some(e)) => fatal!("CreateProcess : {}", ::errno::Errno(e as _)),
}
}
fn setup_pipe(&mut self, ioport: ::winapi::HANDLE) -> ::winapi::HANDLE {
use winapi;
use kernel32;
use errno;
use std::mem::zeroed;
use std::ptr::null_mut;
use widestring::WideCString;
let pipe_name = format!(
"\\\\.\\pipe\\ninja_pid{}_sp{:p}",
unsafe { kernel32::GetCurrentProcessId() },
self
);
let pipe_name = WideCString::from_str(pipe_name).unwrap().into_vec();
self.extra.pipe = unsafe {
kernel32::CreateNamedPipeW(
pipe_name.as_ptr(),
winapi::PIPE_ACCESS_INBOUND | winapi::FILE_FLAG_OVERLAPPED,
winapi::PIPE_TYPE_BYTE,
winapi::PIPE_UNLIMITED_INSTANCES,
0,
0,
winapi::INFINITE,
null_mut(),
)
};
if self.extra.pipe == winapi::INVALID_HANDLE_VALUE {
fatal!("CreateNamedPipe : {}", errno::errno());
}
let create_port_result = unsafe {
kernel32::CreateIoCompletionPort(
self.extra.pipe,
ioport,
self as *mut _ as usize as _,
0,
)
};
if create_port_result.is_null() {
fatal!("CreateIoCompletionPort : {}", errno::errno());
}
self.extra.overlapped = unsafe { zeroed() };
if unsafe {
kernel32::ConnectNamedPipe(self.extra.pipe, &mut self.extra.overlapped as _)
} == winapi::FALSE &&
unsafe { kernel32::GetLastError() } != winapi::ERROR_IO_PENDING
{
fatal!("ConnectNamedPipe : {}", errno::errno());
}
let output_write_handle = unsafe {
kernel32::CreateFileW(
pipe_name.as_ptr(),
winapi::GENERIC_WRITE,
0,
null_mut(),
winapi::OPEN_EXISTING,
0,
null_mut(),
)
};
let mut output_write_child = null_mut();
if unsafe {
kernel32::DuplicateHandle(
kernel32::GetCurrentProcess(),
output_write_handle,
kernel32::GetCurrentProcess(),
&mut output_write_child as _,
0,
winapi::TRUE,
winapi::DUPLICATE_SAME_ACCESS,
)
} == winapi::FALSE
{
fatal!("DuplicateHandle : {}", errno::errno());
}
unsafe {
kernel32::CloseHandle(output_write_handle);
}
output_write_child
}
pub fn on_pipe_ready(&mut self) {
use winapi;
use kernel32;
use errno;
use std::mem::{zeroed, size_of_val};
use std::ptr::null_mut;
let mut bytes = 0 as winapi::DWORD;
if unsafe {
kernel32::GetOverlappedResult(
self.extra.pipe,
&mut self.extra.overlapped as *mut _,
&mut bytes as *mut _,
winapi::TRUE,
)
} == winapi::FALSE
{
if unsafe { kernel32::GetLastError() } == winapi::ERROR_BROKEN_PIPE {
unsafe { kernel32::CloseHandle(self.extra.pipe) };
self.extra.pipe = null_mut();
} else {
fatal!("GetOverlappedResult: {}", errno::errno());
}
return;
}
if self.extra.is_reading && bytes > 0 {
self.buf.extend_from_slice(
&self.extra.overlapped_buf[0..(bytes as usize)],
);
}
self.extra.overlapped = unsafe { zeroed() };
self.extra.is_reading = true;
if unsafe {
kernel32::ReadFile(
self.extra.pipe,
self.extra.overlapped_buf.as_mut_ptr() as usize as _,
size_of_val(&self.extra.overlapped_buf) as _,
&mut bytes as *mut _,
&mut self.extra.overlapped as *mut _,
)
} == winapi::FALSE
{
match unsafe { kernel32::GetLastError() } {
winapi::ERROR_IO_PENDING => {}
winapi::ERROR_BROKEN_PIPE => {
unsafe { kernel32::CloseHandle(self.extra.pipe) };
self.extra.pipe = null_mut();
}
e => {
fatal!("ReadFile : {}", errno::errno());
}
}
return;
}
}
pub fn finish(&mut self) -> ExitStatus {
use winapi;
use kernel32;
if !self.exist() {
return ExitStatus::ExitFailure;
}
unsafe {
kernel32::WaitForSingleObject(self.extra.child, winapi::INFINITE);
}
let mut exit_code = 0 as winapi::DWORD;
unsafe { kernel32::GetExitCodeProcess(self.extra.child, &mut exit_code as _) };
unsafe { kernel32::CloseHandle(self.extra.child) };
self.extra.child = ::std::ptr::null_mut();
match exit_code as _ {
0 => ExitStatus::ExitSuccess,
winapi::STATUS_CONTROL_C_EXIT => ExitStatus::ExitInterrupted,
_ => ExitStatus::ExitFailure,
}
}
fn done(&self) -> bool {
self.extra.pipe.is_null()
}
}
#[cfg(unix)]
impl Subprocess {
pub(super) fn exist(&self) -> bool {
true
}
pub(super) fn start<T>(&mut self, set: &mut SubprocessSet<T>, command: &[u8]) -> bool {
use libc;
use libc_spawn;
use std::mem;
use std::ffi;
use std::ptr;
use errno;
unsafe {
let mut output_pipe: [libc::c_int; 2] = mem::zeroed();
if libc::pipe(output_pipe.as_mut_ptr()) < 0 {
fatal!("pipe: {}", errno::errno());
}
let pipe0 = output_pipe[0];
let pipe1 = output_pipe[1];
self.extra.fd = Some(pipe0);
if !set.use_ppoll() {
if pipe0 >= libc::FD_SETSIZE as _ {
fatal!("pipe: {}", errno::Errno(libc::EMFILE));
}
}
set_close_on_exec(pipe0);
let mut action: libc_spawn::posix_spawn_file_actions_t = mem::zeroed();
if libc_spawn::posix_spawn_file_actions_init(&mut action as _) != 0 {
fatal!("posix_spawn_file_actions_init: {}", errno::errno());
}
if libc_spawn::posix_spawn_file_actions_addclose(&mut action as _, pipe0) != 0 {
fatal!("posix_spawn_file_actions_addclose: {}", errno::errno());
}
let mut attr = mem::zeroed::<libc_spawn::posix_spawnattr_t>();
if libc_spawn::posix_spawnattr_init(&mut attr as _) != 0 {
fatal!("posix_spawnattr_init: {}", errno::errno());
}
let mut flags = 0;
flags |= libc_spawn::POSIX_SPAWN_SETSIGMASK;
if libc_spawn::posix_spawnattr_setsigmask(&mut attr as _, &mut set.extra.old_mask as _) != 0 {
fatal!("posix_spawnattr_setsigmask: {}", errno::errno());
}
if !self.use_console {
flags |= libc_spawn::POSIX_SPAWN_SETPGROUP;
let dev_null = ffi::CString::new("/dev/null").unwrap();
if libc_spawn::posix_spawn_file_actions_addopen(&mut action as _, 0, dev_null.as_ptr(), libc::O_RDONLY, 0) != 0 {
fatal!("posix_spawn_file_actions_addopen: {}", errno::errno());
}
if libc_spawn::posix_spawn_file_actions_adddup2(&mut action as _, pipe1, 1) != 0 {
fatal!("posix_spawn_file_actions_adddup2: {}", errno::errno());
}
if libc_spawn::posix_spawn_file_actions_adddup2(&mut action as _, pipe1, 2) != 0 {
fatal!("posix_spawn_file_actions_adddup2: {}", errno::errno());
}
if libc_spawn::posix_spawn_file_actions_addclose(&mut action as _, pipe1) != 0 {
fatal!("posix_spawn_file_actions_addclose: {}", errno::errno());
}
}
if let Some(v) = libc_spawn::optional_const::posix_spawn_usevfork() {
flags |= v;
}
if libc_spawn::posix_spawnattr_setflags(&mut attr as _, flags) != 0 {
fatal!("posix_spawnattr_setflags: {}", errno::errno());
}
let spawned_args0 = ffi::CString::new("/bin/sh").unwrap();
let spawned_args1 = ffi::CString::new("-c").unwrap();
let spawned_args2 = ffi::CString::from_vec_unchecked(command.to_owned());
let mut spawned_args = [spawned_args0.as_ptr(),
spawned_args1.as_ptr(), spawned_args2.as_ptr(), ptr::null_mut()];
self.extra.pid = Some(-1);
if libc_spawn::posix_spawn(self.extra.pid.as_mut().unwrap() as _,
spawned_args0.as_ptr(), &mut action as _, &mut attr as _,
spawned_args.as_mut_ptr(), libc_spawn::optional_const::environ()) != 0 {
self.extra.pid = None;
fatal!("posix_spawn: {}", errno::errno());
}
if libc_spawn::posix_spawnattr_destroy(&mut attr as _) != 0 {
fatal!("posix_spawnattr_destroy: {}", errno::errno());
}
if libc_spawn::posix_spawn_file_actions_destroy(&mut action as _) != 0 {
fatal!("posix_spawn_file_actions_destroy: {}", errno::errno());
}
libc::close(pipe1);
}
true
}
pub fn on_pipe_ready(&mut self) {
use libc;
use errno;
use std::mem;
unsafe {
let fd = self.extra.fd.unwrap_or(-1);
let mut buf = [0u8; 4096];
let len = libc::read(fd, buf.as_mut_ptr() as usize as _, mem::size_of_val(&buf));
if len < 0 {
fatal!("read: {}", errno::errno());
} else if len > 0 {
self.buf.extend_from_slice(&buf[0..len as usize]);
} else {
libc::close(fd);
self.extra.fd = None;
}
}
}
fn done(&self) -> bool {
self.extra.fd.is_none()
}
pub fn finish(&mut self) -> ExitStatus {
use libc;
use errno;
debug_assert!(self.extra.pid.is_some());
unsafe {
let mut status: libc::c_int = 0;
let pid = self.extra.pid.unwrap();
if libc::waitpid(pid, &mut status as _, 0) < 0 {
fatal!("waitpid({}): {}", pid, errno::errno());
}
self.extra.pid = None;
if libc::WIFEXITED(status) {
let exit = libc::WEXITSTATUS(status);
if exit == 0 {
return ExitStatus::ExitSuccess;
}
} else if libc::WIFSIGNALED(status) {
match libc::WTERMSIG(status) {
libc::SIGINT | libc::SIGTERM | libc::SIGHUP => {
return ExitStatus::ExitInterrupted;
},
_ => {},
}
}
}
return ExitStatus::ExitFailure;
}
}
#[cfg(windows)]
struct SubprocessSetOs {}
#[cfg(windows)]
thread_local! {
static IOPORT : ::std::cell::Cell<::winapi::HANDLE> =
::std::cell::Cell::new(::std::ptr::null_mut());
}
#[cfg(windows)]
unsafe extern "system" fn notify_interrupted(_: ::winapi::DWORD) -> ::winapi::BOOL {
unimplemented!{}
}
#[cfg(windows)]
impl SubprocessSetOs {
pub fn new() -> Self {
use winapi;
use kernel32;
use errno;
use std::ptr::null_mut;
let v = SubprocessSetOs {};
let ioport = unsafe {
kernel32::CreateIoCompletionPort(winapi::INVALID_HANDLE_VALUE, null_mut(), 0, 1)
};
if ioport.is_null() {
fatal!("CreateIoCompletionPort: {}", errno::errno());
}
v.set_ioport(ioport);
if unsafe { kernel32::SetConsoleCtrlHandler(Some(notify_interrupted), winapi::TRUE) } ==
winapi::FALSE
{
fatal!("SetConsoleCtrlHandler: {}", errno::errno());
}
v
}
pub fn ioport(&self) -> ::winapi::HANDLE {
IOPORT.with(|p| p.get())
}
pub fn set_ioport(&self, ioport: ::winapi::HANDLE) {
IOPORT.with(|p| p.set(ioport))
}
}
#[cfg(unix)]
thread_local! {
static INTERRUPTED : ::std::cell::Cell<::libc::c_int> =
::std::cell::Cell::new(0);
}
#[cfg(unix)]
unsafe extern "C" fn set_interrupted_flag(signum: ::libc::c_int) {
INTERRUPTED.with(|x| x.set(signum));
}
#[cfg(unix)]
unsafe fn handle_pending_interruption() {
use libc;
use std::mem;
use errno;
let mut pending = mem::zeroed::<libc::sigset_t>();
libc::sigemptyset(&mut pending as _);
if libc::sigpending(&mut pending as _) == -1 {
fatal!("ninja: sigpending: {}", errno::errno());
}
if libc::sigismember(&mut pending as _, libc::SIGINT) != 0 {
INTERRUPTED.with(|x| x.set(libc::SIGINT));
} else if libc::sigismember(&mut pending as _, libc::SIGTERM) != 0 {
INTERRUPTED.with(|x| x.set(libc::SIGTERM));
} else if libc::sigismember(&mut pending as _, libc::SIGHUP) != 0 {
INTERRUPTED.with(|x| x.set(libc::SIGHUP));
}
}
#[cfg(unix)]
fn is_interrupted() -> bool {
return INTERRUPTED.with(|x| x.get() != 0);
}
#[cfg(unix)]
struct SubprocessSetOs {
old_int_act: ::libc::sigaction,
old_term_act: ::libc::sigaction,
old_hup_act: ::libc::sigaction,
old_mask: ::libc::sigset_t,
}
#[cfg(unix)]
impl SubprocessSetOs {
pub fn new() -> Self {
use std::mem;
use libc;
use errno;
let mut v = unsafe { mem::zeroed::<Self>() };
unsafe {
let mut set = mem::zeroed::<libc::sigset_t>();
libc::sigemptyset(&mut set as _);
libc::sigaddset(&mut set as _, libc::SIGINT);
libc::sigaddset(&mut set as _, libc::SIGTERM);
libc::sigaddset(&mut set as _, libc::SIGHUP);
if libc::sigprocmask(libc::SIG_BLOCK, &mut set as _, &mut v.old_mask as _) < 0 {
fatal!("sigprocmask: {}", errno::errno());
}
let mut act = mem::zeroed::<libc::sigaction>();
act.sa_sigaction = set_interrupted_flag as _;
if libc::sigaction(libc::SIGINT, &mut act as _, &mut v.old_int_act) < 0
|| libc::sigaction(libc::SIGTERM, &mut act as _, &mut v.old_term_act) < 0
|| libc::sigaction(libc::SIGHUP, &mut act as _, &mut v.old_hup_act) < 0 {
fatal!("sigaction: {}", errno::errno());
}
if libc::sigprocmask(libc::SIG_BLOCK, &mut set as _, &mut v.old_mask as _) < 0 {
fatal!("sigprocmask: {}", errno::errno());
}
}
v
}
}
#[cfg(unix)]
impl Drop for SubprocessSetOs {
fn drop(&mut self) {
use libc;
use errno;
use std::ptr;
unsafe {
if libc::sigaction(libc::SIGINT, &mut self.old_int_act, ptr::null_mut()) < 0
|| libc::sigaction(libc::SIGTERM, &mut self.old_term_act, ptr::null_mut()) < 0
|| libc::sigaction(libc::SIGHUP, &mut self.old_hup_act, ptr::null_mut()) < 0 {
fatal!("sigaction: {}", errno::errno());
}
if libc::sigprocmask(libc::SIG_SETMASK, &mut self.old_mask as _, ptr::null_mut()) < 0 {
fatal!("sigprocmask: {}", errno::errno());
}
}
}
}
pub struct SubprocessSet<Data = ()> {
running: HashMap<usize, (Box<Subprocess>, Data)>,
finished: VecDeque<(Box<Subprocess>, Data)>,
extra: SubprocessSetOs,
}
type Iter<'a, Data> = ::std::iter::Chain<
::std::collections::vec_deque::Iter<
'a,
(Box<Subprocess>,
Data),
>,
::std::collections::hash_map::Values<
'a,
usize,
(Box<Subprocess>,
Data),
>,
>;
impl<Data> SubprocessSet<Data> {
pub fn new() -> Self {
SubprocessSet {
running: HashMap::new(),
finished: VecDeque::new(),
extra: SubprocessSetOs::new(),
}
}
pub fn running(&self) -> &HashMap<usize, (Box<Subprocess>, Data)> {
&self.running
}
pub fn finished(&self) -> &VecDeque<(Box<Subprocess>, Data)> {
&self.finished
}
pub fn add(
&mut self,
command: &[u8],
use_console: bool,
data: Data,
) -> Option<&mut (Box<Subprocess>, Data)> {
let mut subprocess = Subprocess::new(use_console);
if !subprocess.start(self, command) {
return None;
}
if subprocess.exist() {
let key = subprocess.as_ref() as *const _ as usize;
self.running.insert(key, (subprocess, data));
return self.running.get_mut(&key);
} else {
self.finished.push_back((subprocess, data));
return self.finished.back_mut();
}
}
pub fn next_finished(&mut self) -> Option<(Box<Subprocess>, Data)> {
self.finished.pop_front()
}
pub fn iter<'a>(&'a self) -> Iter<'a, Data> {
self.finished.iter().chain(self.running.values())
}
pub fn clear(&mut self) {
self.running.clear();
return;
unimplemented!{}
}
}
#[cfg(windows)]
impl<Data> SubprocessSet<Data> {
pub fn do_work(&mut self) -> Result<(), ()> {
use winapi;
use kernel32;
use errno;
use std::ptr::null_mut;
let mut bytes_read = 0 as winapi::DWORD;
let mut subproc = null_mut::<Subprocess>();
let mut overlapped = null_mut::<winapi::OVERLAPPED>();
if unsafe {
kernel32::GetQueuedCompletionStatus(
self.extra.ioport(),
&mut bytes_read as _,
&mut subproc as *mut _ as usize as _,
&mut overlapped as *mut _,
winapi::INFINITE,
)
} == winapi::FALSE
{
if unsafe { kernel32::GetLastError() } != winapi::ERROR_BROKEN_PIPE {
fatal!("GetQueuedCompletionStatus: {}", errno::errno());
}
}
let done = if let Some(subproc) = unsafe { subproc.as_mut() } {
subproc.on_pipe_ready();
subproc.done()
} else {
return Err(());
};
if done {
self.finished.extend(
self.running
.remove(&(subproc as usize))
.into_iter(),
);
}
return Ok(());
}
}
#[cfg(unix)]
impl<Data> SubprocessSet<Data> {
}
#[cfg(all(unix,
not(any(target_env = "uclibc", target_env = "newlib")),
any(target_os = "linux",
target_os = "android",
target_os = "emscripten",
target_os = "fuchsia")))]
impl<Data> SubprocessSet<Data> {
pub fn use_ppoll(&self) -> bool {
true
}
pub fn do_work(&mut self) -> Result<(), ()> {
use std::mem;
use std::ptr;
use libc;
use errno;
unsafe {
let mut fds = Vec::new();
let mut nfds = 0 as libc::nfds_t;
self.running.iter().for_each(|p| {
if let Some(fd) = (p.1).0.extra.fd.clone() {
fds.push(libc::pollfd {
fd,
events: libc::POLLIN | libc::POLLPRI,
revents: 0,
});
nfds += 1;
}
});
INTERRUPTED.with(|x| x.set(0));
let ret = libc::ppoll(fds.as_mut_ptr(), nfds, ptr::null_mut(), &mut self.extra.old_mask as _);
if ret == -1 {
let errno = errno::errno();
if errno.0 != libc::EINTR {
fatal!("ninja: ppoll: {}", errno);
} else if is_interrupted() {
return Err(());
} else {
return Ok(());
}
}
handle_pending_interruption();
if is_interrupted() {
return Err(());
}
let mut removals = Vec::new();
self.running.iter_mut().enumerate().for_each(|(n, p)| {
if let Some(fd) = (p.1).0.extra.fd.clone() {
debug_assert!(fd == fds[n].fd);
if fds[n].revents != 0 {
(p.1).0.on_pipe_ready();
if (p.1).0.done() {
removals.push(*p.0);
}
}
}
});
removals.into_iter().for_each(|p| {
self.finished.extend(self.running.remove(&p));
});
if is_interrupted() {
return Err(());
} else {
return Ok(());
}
}
}
}
#[cfg(all(unix,
any(target_env = "uclibc", target_env = "newlib"),
not(any(target_os = "linux",
target_os = "android",
target_os = "emscripten",
target_os = "fuchsia"))))]
impl<Data> SubprocessSet<Data> {
pub fn use_ppoll(&self) -> bool {
false
}
pub fn do_work(&mut self) -> Result<(), ()> {
use std::mem;
use libc;
use errno;
unsafe {
let mut set = mem::zeroed::<libc::fd_set>();
let mut nfds = 0;
libc::FD_ZERO(&mut set as _);
self.running.iter().for_each(|p| {
if let Some(fd) = (p.1).0.extra.fd.clone() {
libc::FD_SET(fd, &mut set as _);
nfds = std::cmp::max(nfds, fd + 1);
}
});
INTERRUPTED.with(|x| x.set(0));
let ret = libc::pselect(nfds, &mut set as _, 0, 0, 0, &mut self.extra.old_mask as _);
if ret == -1 {
let errno = errno::errno();
if errno.0 != libc::EINTR {
fatal!("ninja: pselect: {}", errno);
} else if is_interrupted() {
return Err(());
} else {
return Ok(());
}
}
handle_pending_interruption();
if is_interrupted() {
return Err(());
}
let mut removals = Vec::new();
self.running.iter_mut().for_each(|p| {
if let Some(fd) = (p.1).0.extra.fd.clone() {
if libc::FD_ISSET(fd, &mut set as _) {
(p.1).0.on_pipe_ready();
if (p.1).0.done() {
removals.push(*p.0);
}
}
}
});
removals.into_iter().for_each(|p| {
self.finished.extend(self.running.remove(&p));
});
if is_interrupted() {
return Err(());
} else {
return Ok(());
}
}
}
}
#[cfg(windows)]
mod imp {
}
#[cfg(unix)]
mod imp {
}