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 super::{table, ProcessRef, ProcessStatus};
|
||||||
use crate::prelude::*;
|
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
|
// Lock the process early to ensure that we do not miss any changes in
|
||||||
// children processes
|
// children processes
|
||||||
let thread = current!();
|
let thread = current!();
|
||||||
@ -36,6 +36,18 @@ pub fn do_wait4(child_filter: &ProcessFilter) -> Result<(pid_t, i32)> {
|
|||||||
return Ok((zombie_pid, exit_status));
|
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);
|
let mut waiter = Waiter::new(child_filter);
|
||||||
process_inner
|
process_inner
|
||||||
.waiting_children_mut()
|
.waiting_children_mut()
|
||||||
@ -63,3 +75,26 @@ fn free_zombie_child(mut parent_inner: SgxMutexGuard<ProcessInner>, zombie_pid:
|
|||||||
let zombie_inner = zombie.inner();
|
let zombie_inner = zombie.inner();
|
||||||
zombie_inner.term_status().unwrap().as_u32() as i32
|
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_clone::CloneFlags;
|
||||||
use super::do_futex::{FutexFlags, FutexOp, FutexTimeout};
|
use super::do_futex::{FutexFlags, FutexOp, FutexTimeout};
|
||||||
use super::do_spawn::FileAction;
|
use super::do_spawn::FileAction;
|
||||||
|
use super::do_wait4::WaitOptions;
|
||||||
use super::prctl::PrctlCmd;
|
use super::prctl::PrctlCmd;
|
||||||
use super::process::ProcessFilter;
|
use super::process::ProcessFilter;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
@ -333,7 +334,7 @@ pub fn do_exit_group(status: i32) -> Result<isize> {
|
|||||||
Ok(0)
|
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() {
|
if !exit_status_ptr.is_null() {
|
||||||
check_mut_ptr(exit_status_ptr)?;
|
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),
|
pid if pid > 0 => ProcessFilter::WithPid(pid as pid_t),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let wait_options =
|
||||||
|
WaitOptions::from_bits(options).ok_or_else(|| errno!(EINVAL, "options not recognized"))?;
|
||||||
let mut exit_status = 0;
|
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)) => {
|
Ok((pid, exit_status)) => {
|
||||||
if !exit_status_ptr.is_null() {
|
if !exit_status_ptr.is_null() {
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -145,7 +145,7 @@ macro_rules! process_syscall_table_with_callback {
|
|||||||
(Vfork = 58) => handle_unsupported(),
|
(Vfork = 58) => handle_unsupported(),
|
||||||
(Execve = 59) => handle_unsupported(),
|
(Execve = 59) => handle_unsupported(),
|
||||||
(Exit = 60) => do_exit(exit_status: i32),
|
(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),
|
(Kill = 62) => do_kill(pid: i32, sig: c_int),
|
||||||
(Uname = 63) => do_uname(name: *mut utsname_t),
|
(Uname = 63) => do_uname(name: *mut utsname_t),
|
||||||
(Semget = 64) => handle_unsupported(),
|
(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 \
|
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 \
|
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 \
|
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
|
# Benchmarks: need to be compiled and run by bench-% target
|
||||||
BENCHES := spawn_and_exit_latency pipe_throughput unix_socket_throughput
|
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