Add support for WNOHANG of waitpid option
This commit is contained in:
parent
ba720dc346
commit
a95e26df42
@ -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
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
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));
|
||||
}
|
Loading…
Reference in New Issue
Block a user