mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-08 21:06:48 +00:00
Add regression tests for exit()
This commit is contained in:
parent
35c20620bc
commit
1ab63c7bcb
@ -18,6 +18,7 @@ TEST_APPS := \
|
||||
epoll \
|
||||
eventfd2 \
|
||||
execve \
|
||||
exit \
|
||||
fdatasync \
|
||||
file_io \
|
||||
fork \
|
||||
|
5
test/apps/exit/Makefile
Normal file
5
test/apps/exit/Makefile
Normal file
@ -0,0 +1,5 @@
|
||||
# SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
include ../test_common.mk
|
||||
|
||||
EXTRA_C_FLAGS := -static -lpthread
|
87
test/apps/exit/exit_code.c
Normal file
87
test/apps/exit/exit_code.c
Normal file
@ -0,0 +1,87 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
#include "../network/test.h"
|
||||
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
struct exit_info {
|
||||
int should_use_exit_group;
|
||||
int should_exit_master_first;
|
||||
};
|
||||
|
||||
static void *thread_slave(void *info_)
|
||||
{
|
||||
struct exit_info *info = info_;
|
||||
|
||||
if (info->should_exit_master_first) {
|
||||
if (info->should_use_exit_group)
|
||||
sleep(3600);
|
||||
usleep(200 * 1000);
|
||||
}
|
||||
|
||||
if (info->should_use_exit_group)
|
||||
syscall(SYS_exit_group, 55);
|
||||
else
|
||||
syscall(SYS_exit, 66);
|
||||
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
static void *thread_master(void *info_)
|
||||
{
|
||||
struct exit_info *info = info_;
|
||||
pthread_t tid;
|
||||
|
||||
CHECK(pthread_create(&tid, NULL, &thread_slave, info));
|
||||
|
||||
if (!info->should_exit_master_first) {
|
||||
if (info->should_use_exit_group)
|
||||
sleep(3600);
|
||||
usleep(200 * 1000);
|
||||
}
|
||||
|
||||
if (info->should_use_exit_group)
|
||||
syscall(SYS_exit_group, 77);
|
||||
else
|
||||
syscall(SYS_exit, 88);
|
||||
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
FN_TEST(exit_two_threads)
|
||||
{
|
||||
struct exit_info info;
|
||||
int stat;
|
||||
|
||||
info.should_use_exit_group = 1;
|
||||
info.should_exit_master_first = 1;
|
||||
if (CHECK(fork()) == 0) {
|
||||
thread_master(&info);
|
||||
}
|
||||
TEST_RES(wait(&stat), WIFEXITED(stat) && WEXITSTATUS(stat) == 77);
|
||||
|
||||
info.should_use_exit_group = 1;
|
||||
info.should_exit_master_first = 0;
|
||||
if (CHECK(fork()) == 0) {
|
||||
thread_master(&info);
|
||||
}
|
||||
TEST_RES(wait(&stat), WIFEXITED(stat) && WEXITSTATUS(stat) == 55);
|
||||
|
||||
info.should_use_exit_group = 0;
|
||||
info.should_exit_master_first = 1;
|
||||
if (CHECK(fork()) == 0) {
|
||||
thread_master(&info);
|
||||
}
|
||||
TEST_RES(wait(&stat), WIFEXITED(stat) && WEXITSTATUS(stat) == 66);
|
||||
|
||||
info.should_use_exit_group = 0;
|
||||
info.should_exit_master_first = 0;
|
||||
if (CHECK(fork()) == 0) {
|
||||
thread_master(&info);
|
||||
}
|
||||
TEST_RES(wait(&stat), WIFEXITED(stat) && WEXITSTATUS(stat) == 88);
|
||||
}
|
||||
END_TEST()
|
97
test/apps/exit/exit_procfs.c
Normal file
97
test/apps/exit/exit_procfs.c
Normal file
@ -0,0 +1,97 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
#include "../network/test.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#define EXIT_PARENT_FIRST ((void *)1)
|
||||
#define EXIT_CHILD_FIRST ((void *)2)
|
||||
|
||||
// TODO: Use `system` directly after we implement `vfork`.
|
||||
static int mini_system(const char *cmd)
|
||||
{
|
||||
int pid;
|
||||
int stat;
|
||||
|
||||
pid = CHECK(fork());
|
||||
|
||||
if (pid == 0) {
|
||||
CHECK(execlp("sh", "sh", "-c", cmd, NULL));
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
CHECK_WITH(waitpid(pid, &stat, 0), _ret == pid && WIFEXITED(stat));
|
||||
|
||||
return WEXITSTATUS(stat);
|
||||
}
|
||||
|
||||
static void *thread_slave(void *arg)
|
||||
{
|
||||
if (arg == EXIT_CHILD_FIRST) {
|
||||
CHECK_WITH(
|
||||
mini_system(
|
||||
"cat /proc/$PPID/status | grep '^Threads:\t2$'"),
|
||||
_ret == 0);
|
||||
|
||||
syscall(SYS_exit, 0);
|
||||
} else {
|
||||
// When the main thread exits, it becomes a zombie, so we still
|
||||
// have two threads.
|
||||
usleep(200 * 1000);
|
||||
CHECK_WITH(
|
||||
mini_system(
|
||||
"cat /proc/$PPID/status | grep '^Threads:\t2$'"),
|
||||
_ret == 0);
|
||||
|
||||
syscall(SYS_exit, 0);
|
||||
}
|
||||
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
static void thread_master(void *arg)
|
||||
{
|
||||
pthread_t tid;
|
||||
|
||||
CHECK(pthread_create(&tid, NULL, &thread_slave, arg));
|
||||
|
||||
if (arg == EXIT_PARENT_FIRST) {
|
||||
CHECK_WITH(
|
||||
mini_system(
|
||||
"cat /proc/$PPID/status | grep '^Threads:\t2$'"),
|
||||
_ret == 0);
|
||||
|
||||
syscall(SYS_exit, 0);
|
||||
} else {
|
||||
// When a non-main thread exits, its resource is automatically
|
||||
// freed, so we only have one thread.
|
||||
usleep(200 * 1000);
|
||||
CHECK_WITH(
|
||||
mini_system(
|
||||
"cat /proc/$PPID/status | grep '^Threads:\t1$'"),
|
||||
_ret == 0);
|
||||
|
||||
syscall(SYS_exit, 0);
|
||||
}
|
||||
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
FN_TEST(exit_procfs)
|
||||
{
|
||||
int stat;
|
||||
|
||||
if (CHECK(fork()) == 0) {
|
||||
thread_master(EXIT_CHILD_FIRST);
|
||||
}
|
||||
TEST_RES(wait(&stat), WIFEXITED(stat) && WEXITSTATUS(stat) == 0);
|
||||
|
||||
if (CHECK(fork()) == 0) {
|
||||
thread_master(EXIT_PARENT_FIRST);
|
||||
}
|
||||
TEST_RES(wait(&stat), WIFEXITED(stat) && WEXITSTATUS(stat) == 0);
|
||||
}
|
||||
END_TEST()
|
@ -15,6 +15,8 @@ clone3/clone_no_exit_signal
|
||||
clone3/clone_process
|
||||
cpu_affinity/cpu_affinity
|
||||
execve/execve
|
||||
exit/exit_code
|
||||
exit/exit_procfs
|
||||
eventfd2/eventfd2
|
||||
fork/fork
|
||||
fork_c/fork
|
||||
|
Loading…
x
Reference in New Issue
Block a user