Add support for WNOHANG of waitpid option

This commit is contained in:
Hui, Chunyang 2021-04-28 08:23:19 +00:00 committed by Zongmin.Gu
parent ba720dc346
commit a95e26df42
6 changed files with 152 additions and 5 deletions

@ -3,7 +3,7 @@ use super::wait::Waiter;
use super::{table, ProcessRef, ProcessStatus};
use crate::prelude::*;
pub fn do_wait4(child_filter: &ProcessFilter) -> Result<(pid_t, i32)> {
pub fn do_wait4(child_filter: &ProcessFilter, options: WaitOptions) -> Result<(pid_t, i32)> {
// Lock the process early to ensure that we do not miss any changes in
// children processes
let thread = current!();
@ -36,6 +36,18 @@ pub fn do_wait4(child_filter: &ProcessFilter) -> Result<(pid_t, i32)> {
return Ok((zombie_pid, exit_status));
}
// TODO: Support these options
if !options.supported() {
warn!("Unsupported options contained. wait options: {:?}", options);
}
// If the WNOHANG bit is set in OPTIONS, and that child
// is not already dead, return (pid_t) 0. If successful,
// return PID and store the dead child's status in STAT_LOC.
if options.contains(WaitOptions::WNOHANG) {
return Ok((0, 0));
}
let mut waiter = Waiter::new(child_filter);
process_inner
.waiting_children_mut()
@ -63,3 +75,26 @@ fn free_zombie_child(mut parent_inner: SgxMutexGuard<ProcessInner>, zombie_pid:
let zombie_inner = zombie.inner();
zombie_inner.term_status().unwrap().as_u32() as i32
}
// Based on waitflags.h
// WNOWAIT is not listed here which can only be used in "waitid" syscall.
bitflags! {
pub struct WaitOptions: u32 {
const WNOHANG = 0x1;
//Note: Below flags are not supported yet
const WSTOPPED = 0x2; // Same as WUNTRACED
const WEXITED = 0x4;
const WCONTINUED = 0x8;
}
}
impl WaitOptions {
fn supported(&self) -> bool {
let unsupported_flags = WaitOptions::all() - WaitOptions::WNOHANG;
!self.intersects(unsupported_flags)
}
}
// Based on waitstatus.h
const WAIT_STATUS_STOPPED: i32 = 0x7f;
const WAIT_STATUS_CONTINUED: i32 = 0xffff;

@ -2,6 +2,7 @@ use super::do_arch_prctl::ArchPrctlCode;
use super::do_clone::CloneFlags;
use super::do_futex::{FutexFlags, FutexOp, FutexTimeout};
use super::do_spawn::FileAction;
use super::do_wait4::WaitOptions;
use super::prctl::PrctlCmd;
use super::process::ProcessFilter;
use crate::prelude::*;
@ -333,7 +334,7 @@ pub fn do_exit_group(status: i32) -> Result<isize> {
Ok(0)
}
pub fn do_wait4(pid: i32, exit_status_ptr: *mut i32) -> Result<isize> {
pub fn do_wait4(pid: i32, exit_status_ptr: *mut i32, options: u32) -> Result<isize> {
if !exit_status_ptr.is_null() {
check_mut_ptr(exit_status_ptr)?;
}
@ -348,8 +349,11 @@ pub fn do_wait4(pid: i32, exit_status_ptr: *mut i32) -> Result<isize> {
pid if pid > 0 => ProcessFilter::WithPid(pid as pid_t),
_ => unreachable!(),
};
let wait_options =
WaitOptions::from_bits(options).ok_or_else(|| errno!(EINVAL, "options not recognized"))?;
let mut exit_status = 0;
match super::do_wait4::do_wait4(&child_process_filter) {
match super::do_wait4::do_wait4(&child_process_filter, wait_options) {
Ok((pid, exit_status)) => {
if !exit_status_ptr.is_null() {
unsafe {

@ -145,7 +145,7 @@ macro_rules! process_syscall_table_with_callback {
(Vfork = 58) => handle_unsupported(),
(Execve = 59) => handle_unsupported(),
(Exit = 60) => do_exit(exit_status: i32),
(Wait4 = 61) => do_wait4(pid: i32, _exit_status: *mut i32),
(Wait4 = 61) => do_wait4(pid: i32, _exit_status: *mut i32, options: u32),
(Kill = 62) => do_kill(pid: i32, sig: c_int),
(Uname = 63) => do_uname(name: *mut utsname_t),
(Semget = 64) => handle_unsupported(),

@ -18,7 +18,7 @@ TEST_DEPS := client data_sink
TESTS ?= env empty hello_world malloc mmap file fs_perms getpid spawn sched pipe time \
truncate readdir mkdir open stat link symlink chmod chown tls pthread uname rlimit \
server server_epoll unix_socket cout hostfs cpuid rdtsc device sleep exit_group \
ioctl fcntl eventfd emulate_syscall access signal sysinfo prctl rename procfs
ioctl fcntl eventfd emulate_syscall access signal sysinfo prctl rename procfs wait
# Benchmarks: need to be compiled and run by bench-% target
BENCHES := spawn_and_exit_latency pipe_throughput unix_socket_throughput

5
test/wait/Makefile Normal file

@ -0,0 +1,5 @@
include ../test_common.mk
EXTRA_C_FLAGS := -Wno-unused-function
EXTRA_LINK_FLAGS :=
BIN_ARGS :=

103
test/wait/main.c Normal file

@ -0,0 +1,103 @@
#define _GNU_SOURCE
#include <sys/wait.h>
#include <errno.h>
#include <spawn.h>
#include <stdbool.h>
#include <stdlib.h>
#include "test.h"
static int test_wait_no_children() {
int status = 0;
int ret = wait(&status);
if (ret != -1 || errno != ECHILD) {
THROW_ERROR("wait no children error");
}
return 0;
}
static int test_wait_nohang() {
int status = 0;
int ret = waitpid(-1, &status, WNOHANG);
if (ret != -1 || errno != ECHILD) {
THROW_ERROR("wait no children with NOHANG error");
}
int child_pid = 0;
// /bin/sleep lasts more than 1 sec
if (posix_spawn(&child_pid, "/bin/sleep", NULL, NULL, NULL, NULL) < 0) {
THROW_ERROR("posix_spawn child error");
}
ret = waitpid(child_pid, &status, WNOHANG);
if (ret != 0) {
THROW_ERROR("wait child with NOHANG error");
}
sleep(2);
// The child process should exit
ret = waitpid(child_pid, &status, WNOHANG);
if (ret != child_pid) {
THROW_ERROR("wait child with NOHANG error");
}
return 0;
}
// NOTE: WUNTRACED is same as WSTOPPED
// TODO: Support WUNTRACED and WCONTINUED and enable this test case
static int test_wait_untraced_and_continued() {
int status = 0;
int ret = waitpid(-1, &status, WNOHANG);
if (ret != -1 || errno != ECHILD) {
THROW_ERROR("wait no children with NOHANG error");
}
int child_pid = 0;
if (posix_spawn(&child_pid, "/bin/sleep", NULL, NULL, NULL, NULL) < 0) {
THROW_ERROR("posix_spawn child error");
}
ret = waitpid(child_pid, &status, WNOHANG);
if (ret != 0) {
THROW_ERROR("wait child with NOHANG error");
}
kill(child_pid, SIGSTOP);
// WUNTRACED will get child_pid status
ret = waitpid(child_pid, &status, WUNTRACED);
printf("ret = %d, status = %d\n", ret, status);
if (ret != child_pid || !WIFSTOPPED(status) || WSTOPSIG(status) != SIGSTOP ) {
THROW_ERROR("wait child status error");
}
// Let child get back to running by sending SIGCONT
kill(child_pid, SIGCONT);
ret = waitpid(child_pid, &status, WCONTINUED);
printf("ret = %d, status = %d\n", ret, status);
if (ret != child_pid || !WIFCONTINUED(status)) {
THROW_ERROR("wait child status error");
}
sleep(2);
// The child process should exit
ret = waitpid(child_pid, &status, WNOHANG | WUNTRACED);
printf("ret = %d, status = %d\n", ret, status);
if (ret != child_pid || !WIFEXITED(status) ) {
THROW_ERROR("wait child with NOHANG error");
}
return 0;
}
// ============================================================================
// Test suite main
// ============================================================================
static test_case_t test_cases[] = {
TEST_CASE(test_wait_no_children),
TEST_CASE(test_wait_nohang),
// TODO: Enable this test case
// TEST_CASE(test_wait_untraced_and_continued),
};
int main(int argc, const char *argv[]) {
return test_suite_run(test_cases, ARRAY_SIZE(test_cases));
}