mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-23 09:23:25 +00:00
336 lines
7.3 KiB
C
336 lines
7.3 KiB
C
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
#include "../network/test.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
#include <unistd.h>
|
|
#include <signal.h>
|
|
#include <linux/wait.h>
|
|
#include <pthread.h>
|
|
#include <sys/mman.h>
|
|
#include <fcntl.h>
|
|
|
|
static pid_t pid;
|
|
static int status;
|
|
|
|
FN_SETUP(fork_child)
|
|
{
|
|
pid = CHECK(fork());
|
|
|
|
if (pid == 0) {
|
|
// Child entering an infinite loop until killed by parent.
|
|
while (1) {
|
|
usleep(100);
|
|
}
|
|
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
|
|
// Parent process
|
|
sleep(1); // Ensure the child process is running
|
|
}
|
|
END_SETUP()
|
|
|
|
FN_TEST(stop_child)
|
|
{
|
|
// Stop the child process
|
|
TEST_SUCC(kill(pid, SIGSTOP));
|
|
TEST_RES(wait4(pid, &status, WSTOPPED, NULL),
|
|
_ret == pid && WIFSTOPPED(status) &&
|
|
WSTOPSIG(status) == SIGSTOP);
|
|
status = 0;
|
|
TEST_RES(wait4(pid, &status, WSTOPPED | WNOHANG, NULL),
|
|
_ret == 0 && status == 0);
|
|
}
|
|
END_TEST()
|
|
|
|
FN_TEST(continue_child)
|
|
{
|
|
TEST_SUCC(kill(pid, SIGCONT));
|
|
status = 0;
|
|
TEST_RES(wait4(pid, &status, WCONTINUED, NULL),
|
|
_ret == pid && WIFCONTINUED(status));
|
|
status = 0;
|
|
TEST_RES(wait4(pid, &status, WCONTINUED | WNOHANG, NULL),
|
|
_ret == 0 && status == 0);
|
|
}
|
|
END_TEST()
|
|
|
|
FN_TEST(wait_nowait)
|
|
{
|
|
TEST_SUCC(kill(pid, SIGSTOP));
|
|
|
|
status = 0;
|
|
TEST_ERRNO(wait4(pid, &status, WSTOPPED | WNOWAIT, NULL), EINVAL);
|
|
status = 0;
|
|
TEST_RES(wait4(pid, &status, WSTOPPED, NULL),
|
|
_ret == pid && WIFSTOPPED(status) &&
|
|
WSTOPSIG(status) == SIGSTOP);
|
|
status = 0;
|
|
TEST_RES(wait4(pid, &status, WSTOPPED | WNOHANG, NULL),
|
|
_ret == 0 && status == 0);
|
|
|
|
TEST_SUCC(kill(pid, SIGCONT));
|
|
|
|
status = 0;
|
|
TEST_ERRNO(wait4(pid, &status, WCONTINUED | WNOWAIT, NULL), EINVAL);
|
|
status = 0;
|
|
TEST_RES(wait4(pid, &status, WCONTINUED, NULL),
|
|
_ret == pid && WIFCONTINUED(status));
|
|
status = 0;
|
|
TEST_RES(wait4(pid, &status, WCONTINUED | WNOHANG, NULL),
|
|
_ret == 0 && status == 0);
|
|
}
|
|
END_TEST()
|
|
|
|
FN_TEST(wait_stopped_and_continued)
|
|
{
|
|
TEST_SUCC(kill(pid, SIGSTOP));
|
|
sleep(1);
|
|
TEST_SUCC(kill(pid, SIGCONT));
|
|
sleep(1);
|
|
|
|
status = 0;
|
|
TEST_RES(wait4(pid, &status, WSTOPPED | WCONTINUED, NULL),
|
|
_ret == pid && WIFCONTINUED(status));
|
|
status = 0;
|
|
TEST_RES(wait4(pid, &status, WSTOPPED | WCONTINUED | WNOHANG, NULL),
|
|
_ret == 0 && status == 0);
|
|
}
|
|
END_TEST()
|
|
|
|
FN_TEST(continue_not_stopped)
|
|
{
|
|
TEST_SUCC(kill(pid, SIGCONT));
|
|
status = 0;
|
|
TEST_RES(wait4(pid, &status, WCONTINUED | WNOHANG, NULL),
|
|
_ret == 0 && status == 0);
|
|
}
|
|
END_TEST()
|
|
|
|
FN_TEST(stop_continue_continue)
|
|
{
|
|
TEST_SUCC(kill(pid, SIGSTOP));
|
|
sleep(1);
|
|
TEST_SUCC(kill(pid, SIGCONT));
|
|
sleep(1);
|
|
TEST_SUCC(kill(pid, SIGCONT));
|
|
sleep(1);
|
|
|
|
status = 0;
|
|
TEST_RES(wait4(pid, &status, WSTOPPED | WNOHANG, NULL),
|
|
status == 0 && _ret == 0);
|
|
status = 0;
|
|
TEST_RES(wait4(pid, &status, WSTOPPED | WCONTINUED, NULL),
|
|
_ret == pid && WIFCONTINUED(status));
|
|
status = 0;
|
|
TEST_RES(wait4(pid, &status, WSTOPPED | WCONTINUED | WNOHANG, NULL),
|
|
_ret == 0 && status == 0);
|
|
}
|
|
END_TEST()
|
|
|
|
FN_TEST(stop_continue_stop)
|
|
{
|
|
TEST_SUCC(kill(pid, SIGSTOP));
|
|
sleep(1);
|
|
TEST_SUCC(kill(pid, SIGCONT));
|
|
sleep(1);
|
|
TEST_SUCC(kill(pid, SIGSTOP));
|
|
sleep(1);
|
|
|
|
status = 0;
|
|
TEST_RES(wait4(pid, &status, WCONTINUED | WNOHANG, NULL), status == 0);
|
|
status = 0;
|
|
TEST_RES(wait4(pid, &status, WSTOPPED, NULL),
|
|
_ret == pid && WIFSTOPPED(status) &&
|
|
WSTOPSIG(status) == SIGSTOP);
|
|
status = 0;
|
|
TEST_RES(wait4(pid, &status, WSTOPPED | WNOHANG, NULL),
|
|
_ret == 0 && status == 0);
|
|
|
|
// Restore the state
|
|
TEST_SUCC(kill(pid, SIGCONT));
|
|
status = 0;
|
|
TEST_RES(wait4(pid, &status, WCONTINUED, NULL),
|
|
_ret == pid && WIFCONTINUED(status));
|
|
status = 0;
|
|
TEST_RES(wait4(pid, &status, WCONTINUED | WSTOPPED | WNOHANG, NULL),
|
|
_ret == 0 && status == 0);
|
|
}
|
|
END_TEST()
|
|
|
|
FN_TEST(stop_stopped)
|
|
{
|
|
TEST_SUCC(kill(pid, SIGSTOP));
|
|
status = 0;
|
|
TEST_RES(wait4(pid, &status, WSTOPPED, NULL),
|
|
_ret == pid && WIFSTOPPED(status) &&
|
|
WSTOPSIG(status) == SIGSTOP);
|
|
|
|
TEST_SUCC(kill(pid, SIGSTOP));
|
|
sleep(1);
|
|
status = 0;
|
|
TEST_RES(wait4(pid, &status, WSTOPPED | WNOHANG, NULL),
|
|
_ret == 0 && status == 0);
|
|
}
|
|
END_TEST()
|
|
|
|
void *thread_func(void *arg)
|
|
{
|
|
while (1) {
|
|
sleep(0.1);
|
|
}
|
|
}
|
|
|
|
void child_process()
|
|
{
|
|
pthread_t t1, t2;
|
|
|
|
CHECK(pthread_create(&t1, NULL, thread_func, NULL));
|
|
CHECK(pthread_create(&t2, NULL, thread_func, NULL));
|
|
|
|
pthread_join(t1, NULL);
|
|
pthread_join(t2, NULL);
|
|
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
|
|
FN_TEST(multithread)
|
|
{
|
|
pid_t child_pid = TEST_SUCC(fork());
|
|
|
|
if (child_pid == 0) {
|
|
child_process();
|
|
}
|
|
|
|
sleep(1);
|
|
|
|
TEST_SUCC(kill(child_pid, SIGSTOP));
|
|
int status = 0;
|
|
TEST_RES(wait4(child_pid, &status, WSTOPPED, NULL),
|
|
_ret == child_pid && WIFSTOPPED(status) &&
|
|
WSTOPSIG(status) == SIGSTOP);
|
|
TEST_RES(wait4(child_pid, &status, WSTOPPED | WNOHANG, NULL),
|
|
_ret == 0);
|
|
|
|
TEST_SUCC(kill(child_pid, SIGKILL));
|
|
TEST_RES(wait4(child_pid, NULL, 0, NULL), _ret == child_pid);
|
|
}
|
|
END_TEST()
|
|
|
|
volatile int sigint_counter = 0;
|
|
volatile int sigtrap_counter = 0;
|
|
|
|
void handle_sigint(int signum, siginfo_t *_info, void *_context)
|
|
{
|
|
sigint_counter += 1;
|
|
}
|
|
|
|
void handle_sigtrap(int signum, siginfo_t *_info, void *_context)
|
|
{
|
|
sigtrap_counter += 1;
|
|
}
|
|
|
|
void child_process2(int *pipe_fds)
|
|
{
|
|
CHECK(close(pipe_fds[0]));
|
|
|
|
struct sigaction new_action = {};
|
|
new_action.sa_sigaction = handle_sigint;
|
|
CHECK(sigaction(SIGINT, &new_action, NULL));
|
|
|
|
new_action.sa_sigaction = handle_sigtrap;
|
|
CHECK(sigaction(SIGTRAP, &new_action, NULL));
|
|
|
|
while (1) {
|
|
usleep(100);
|
|
if (sigint_counter == 1) {
|
|
sigint_counter = 0;
|
|
CHECK(write(pipe_fds[1], "a", 1));
|
|
}
|
|
if (sigtrap_counter == 1) {
|
|
sigtrap_counter = 0;
|
|
CHECK(write(pipe_fds[1], "b", 1));
|
|
}
|
|
}
|
|
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
|
|
FN_TEST(nested_signals)
|
|
{
|
|
int pipe_fds[2];
|
|
TEST_SUCC(pipe(pipe_fds));
|
|
|
|
for (int i = 0; i < 2; i++) {
|
|
int fd = pipe_fds[i];
|
|
int flags = TEST_SUCC(fcntl(fd, F_GETFL, 0));
|
|
TEST_SUCC(fcntl(fd, F_SETFL, flags | O_NONBLOCK));
|
|
}
|
|
|
|
int child_pid = TEST_SUCC(fork());
|
|
|
|
if (child_pid == 0) {
|
|
child_process2(pipe_fds);
|
|
}
|
|
|
|
TEST_SUCC(close(pipe_fds[1]));
|
|
sleep(1);
|
|
|
|
char buf[1] = { 0 };
|
|
|
|
// SIGINT -> SIGTRAP
|
|
TEST_SUCC(kill(child_pid, SIGINT));
|
|
sleep(1);
|
|
TEST_RES(read(pipe_fds[0], buf, 1), _ret == 1 && buf[0] == 'a');
|
|
|
|
TEST_SUCC(kill(child_pid, SIGTRAP));
|
|
sleep(1);
|
|
TEST_RES(read(pipe_fds[0], buf, 1), _ret == 1 && buf[0] == 'b');
|
|
|
|
// SIGSTOP -> SIGINT -> SIGTRAP -> SIGCONT
|
|
TEST_SUCC(kill(child_pid, SIGSTOP));
|
|
sleep(1);
|
|
|
|
// FIXME: The following two read pipe checks are commented out because
|
|
// Asterinas currently handles signals with user-provided handlers
|
|
// when the thread is stopped, while Linux does not.
|
|
TEST_SUCC(kill(child_pid, SIGINT));
|
|
sleep(1);
|
|
// TEST_ERRNO(read(pipe_fds[0], buf, 1), EAGAIN);
|
|
|
|
TEST_SUCC(kill(child_pid, SIGTRAP));
|
|
sleep(1);
|
|
// TEST_ERRNO(read(pipe_fds[0], buf, 1), EAGAIN);
|
|
|
|
TEST_RES(wait4(child_pid, &status, WSTOPPED, NULL),
|
|
_ret == child_pid && WIFSTOPPED(status) &&
|
|
WSTOPSIG(status) == SIGSTOP);
|
|
|
|
TEST_SUCC(kill(child_pid, SIGCONT));
|
|
sleep(1);
|
|
TEST_RES(read(pipe_fds[0], buf, 1), _ret == 1 && buf[0] == 'a');
|
|
TEST_RES(read(pipe_fds[0], buf, 1), _ret == 1 && buf[0] == 'b');
|
|
|
|
TEST_RES(wait4(child_pid, &status, WCONTINUED, NULL),
|
|
_ret == child_pid && WIFCONTINUED(status));
|
|
|
|
// SIGKILL
|
|
TEST_SUCC(kill(child_pid, SIGKILL));
|
|
TEST_RES(wait4(child_pid, NULL, 0, NULL), _ret == child_pid);
|
|
}
|
|
END_TEST()
|
|
|
|
FN_SETUP(kill_stopped)
|
|
{
|
|
CHECK(kill(pid, SIGKILL));
|
|
sleep(1);
|
|
CHECK_WITH(wait4(pid, &status, WSTOPPED, NULL),
|
|
_ret == pid && WIFSIGNALED(status) &&
|
|
WTERMSIG(status) == SIGKILL);
|
|
}
|
|
END_SETUP()
|