From f7cad70da1a10afa9bc53ff512f4b7bfc20b3298 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Sun, 12 Nov 2023 05:41:30 -0800 Subject: [PATCH] Introduce posix tests package --- Makefile | 1 + test/posix/mask_test.c | 115 +++++++++++++++++++++++++++++ test/posix/reentrant_signal_test.c | 79 ++++++++++++++++++++ test/posix/sa_resethand_test.c | 39 ++++++++++ test/posix/sigchld_test.c | 104 ++++++++++++++++++++++++++ test/posix/signal_test.c | 55 ++++++++++++++ test/posix/test.mk | 59 +++++++++++++++ test/test.mk | 1 + 8 files changed, 453 insertions(+) create mode 100644 test/posix/mask_test.c create mode 100644 test/posix/reentrant_signal_test.c create mode 100644 test/posix/sa_resethand_test.c create mode 100644 test/posix/sigchld_test.c create mode 100644 test/posix/signal_test.c create mode 100644 test/posix/test.mk diff --git a/Makefile b/Makefile index 81f00955a..be7ff06a3 100644 --- a/Makefile +++ b/Makefile @@ -221,6 +221,7 @@ include third_party/nsync/testing/testing.mk include libc/testlib/testlib.mk include tool/viz/lib/vizlib.mk include tool/args/args.mk +include test/posix/test.mk include test/tool/args/test.mk include third_party/linenoise/linenoise.mk include third_party/maxmind/maxmind.mk diff --git a/test/posix/mask_test.c b/test/posix/mask_test.c new file mode 100644 index 000000000..f8aa6e00c --- /dev/null +++ b/test/posix/mask_test.c @@ -0,0 +1,115 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2023 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include +#include +#include +#include + +volatile sig_atomic_t cpu_signal_received = 0; +volatile sig_atomic_t io_signal_received = 0; + +// Declare thread IDs +pthread_t cpu_thread_id; +pthread_t io_thread_id; + +void signal_handler(int signum) { + // Set the flag to indicate the signal was received + if (pthread_equal(pthread_self(), cpu_thread_id)) { + cpu_signal_received = 1; + } else if (pthread_equal(pthread_self(), io_thread_id)) { + io_signal_received = 1; + } +} + +void *cpu_bound_task(void *arg) { + // Unblock SIGUSR1 for this thread + sigset_t set; + sigemptyset(&set); + sigaddset(&set, SIGUSR1); + pthread_sigmask(SIG_UNBLOCK, &set, NULL); + + // Simulate CPU-bound work + while (!cpu_signal_received) { + // Busy-wait loop + } + return NULL; +} + +void *io_bound_task(void *arg) { + // Unblock SIGUSR1 for this thread + sigset_t set; + sigemptyset(&set); + sigaddset(&set, SIGUSR1); + pthread_sigmask(SIG_UNBLOCK, &set, NULL); + + // Simulate I/O-bound work + sleep(1); // Use sleep to simulate waiting for I/O + return NULL; +} + +int main() { + // Install the signal handler + struct sigaction sa; + sa.sa_handler = signal_handler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + + if (sigaction(SIGUSR1, &sa, NULL) == -1) { + // Failed to install signal handler + exit(1); + } + + // Block SIGUSR1 in the main thread + sigset_t set; + sigemptyset(&set); + sigaddset(&set, SIGUSR1); + if (pthread_sigmask(SIG_BLOCK, &set, NULL) != 0) { + // Failed to set signal mask + exit(2); + } + + // Create the CPU-bound and I/O-bound threads + if (pthread_create(&cpu_thread_id, NULL, &cpu_bound_task, NULL) != 0) { + // Failed to create the CPU-bound thread + exit(3); + } + if (pthread_create(&io_thread_id, NULL, &io_bound_task, NULL) != 0) { + // Failed to create the I/O-bound thread + exit(4); + } + + /* sleep(1); */ + + // Raise the signal for both threads + pthread_kill(cpu_thread_id, SIGUSR1); + pthread_kill(io_thread_id, SIGUSR1); + + // Wait for both threads to finish + pthread_join(cpu_thread_id, NULL); + pthread_join(io_thread_id, NULL); + + // Check if both threads received the signal + if (cpu_signal_received && io_signal_received) { + // Both signals were successfully caught + exit(0); + } else { + // One or both signals were not caught + exit(5); + } +} diff --git a/test/posix/reentrant_signal_test.c b/test/posix/reentrant_signal_test.c new file mode 100644 index 000000000..5fb182db9 --- /dev/null +++ b/test/posix/reentrant_signal_test.c @@ -0,0 +1,79 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2023 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include +#include +#include +#include + +volatile sig_atomic_t signal_handled_count; + +void reentrant_signal_handler(int signum) { + + // waste stack memory to test raise() won't recurse + volatile char burn_stack[3500]; + burn_stack[3000] = 0; + burn_stack[0] = 3; + + // Increment the count to indicate the signal was handled + if (++signal_handled_count == 10000) return; + + // Re-raise the signal to test reentrancy + raise(signum | burn_stack[3000]); +} + +void *child_thread_func(void *arg) { + // Send SIGUSR2 to the main thread + pthread_kill(*((pthread_t *)arg), SIGUSR2); + return NULL; +} + +int main() { + pthread_t main_thread_id = pthread_self(); + pthread_t child_thread; + struct sigaction sa; + + // Install the signal handler for SIGUSR2 + sa.sa_handler = reentrant_signal_handler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + if (sigaction(SIGUSR2, &sa, NULL) == -1) { + exit(1); // Failed to install signal handler + } + + // Create the child thread + if (pthread_create(&child_thread, NULL, &child_thread_func, + &main_thread_id) != 0) { + exit(2); // Failed to create child thread + } + + // Wait for the signal to be handled + while (signal_handled_count < 10000) { + // Busy wait + } + + // Wait for child thread to finish + pthread_join(child_thread, NULL); + + // Check if the signal was handled reentrantly + if (signal_handled_count == 10000) { + exit(0); // Success + } else { + exit(3); // The signal was not handled twice as expected + } +} diff --git a/test/posix/sa_resethand_test.c b/test/posix/sa_resethand_test.c new file mode 100644 index 000000000..431bf87f3 --- /dev/null +++ b/test/posix/sa_resethand_test.c @@ -0,0 +1,39 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2023 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include + +volatile int gotsig; + +void OnSig(int sig) { + gotsig = sig; +} + +int main() { + struct sigaction sa; + sa.sa_handler = OnSig; + sa.sa_flags = SA_RESETHAND; + sigemptyset(&sa.sa_mask); + if (sigaction(SIGUSR1, &sa, 0)) return 1; + if (sigaction(SIGUSR1, 0, &sa)) return 2; + if (sa.sa_handler != OnSig) return 3; + if (raise(SIGUSR1)) return 4; + if (gotsig != SIGUSR1) return 5; + if (sigaction(SIGUSR1, 0, &sa)) return 6; + if (sa.sa_handler != SIG_DFL) return 7; +} diff --git a/test/posix/sigchld_test.c b/test/posix/sigchld_test.c new file mode 100644 index 000000000..a8e40eda8 --- /dev/null +++ b/test/posix/sigchld_test.c @@ -0,0 +1,104 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2023 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// clang-format off +// sh -c 'build/bootstrap/make.com -j8 V=1 o//test/posix/sigchld_test.com.runs' +// clang-format on + +void Assert(const char *file, int line, bool ok) { + sigset_t ss; + if (!ok) { + sigfillset(&ss); + sigprocmask(SIG_BLOCK, &ss, 0); + fprintf(stderr, "%s:%d: assertion failed\n", file, line); + _exit(1); + } +} + +#define FL __FILE__, __LINE__ +#define ASSERT_TRUE(got) Assert(FL, got) +#define EXPECT_TRUE(got) Assert(FL, got) +#define EXPECT_EQ(want, got) Assert(FL, (want) == (got)) +#define ASSERT_EQ(want, got) Assert(FL, (want) == (got)) +#define EXPECT_NE(donotwant, got) Assert(FL, (donotwant) != (got)) +#define ASSERT_SYS(err, want, got) \ + errno = 0, Assert(FL, (want) == (got)), Assert(FL, (err) == errno) +#define EXPECT_SYS(err, want, got) \ + errno = 0, Assert(FL, (want) == (got)), Assert(FL, (err) == errno) + +int sigchld_pid; +volatile bool sigchld_got_signal; + +// 1. Test SIGCHLD is delivered due to lack of waiters. +// 2. Test wait4() returns ECHILD when nothing remains. +// 3. Test wait4() failing doesn't clobber *ws and *ru. +// 4. Check information passed in siginfo and ucontext. +void OnSigchld(int sig, siginfo_t *si, void *arg) { + int ws; + struct rusage ru; + ucontext_t *ctx = arg; + EXPECT_NE(-1, wait4(sigchld_pid, &ws, 0, &ru)); + EXPECT_SYS(ECHILD, -1, wait4(sigchld_pid, &ws, 0, &ru)); + EXPECT_TRUE(WIFEXITED(ws)); + EXPECT_EQ(42, WEXITSTATUS(ws)); + EXPECT_EQ(SIGCHLD, sig); + EXPECT_EQ(SIGCHLD, si->si_signo); + EXPECT_EQ(CLD_EXITED, si->si_code); + EXPECT_EQ(sigchld_pid, si->si_pid); + EXPECT_EQ(getuid(), si->si_uid); + EXPECT_NE(NULL, ctx); + sigchld_got_signal = true; +} + +int main(int argc, char *argv[]) { + const char *startup = getenv("EXITCODE"); + if (startup) exit(atoi(startup)); + struct sigaction newsa, oldsa; + sigset_t oldmask, blocksigchld, unblockall; + char *prog = argv[0]; + char *args[] = {prog, NULL}; + char *envs[] = {"EXITCODE=42", NULL}; + newsa.sa_flags = SA_SIGINFO; + newsa.sa_sigaction = OnSigchld; + sigemptyset(&newsa.sa_mask); + ASSERT_SYS(0, 0, sigaction(SIGCHLD, &newsa, &oldsa)); + sigemptyset(&blocksigchld); + sigaddset(&blocksigchld, SIGCHLD); + ASSERT_SYS(0, 0, sigprocmask(SIG_BLOCK, &blocksigchld, &oldmask)); + EXPECT_EQ(0, posix_spawn(&sigchld_pid, prog, NULL, NULL, args, envs)); + sigemptyset(&unblockall); + EXPECT_SYS(EINTR, -1, sigsuspend(&unblockall)); + EXPECT_TRUE(sigchld_got_signal); + EXPECT_SYS(0, 0, sigaction(SIGCHLD, &oldsa, 0)); + ASSERT_SYS(0, 0, sigprocmask(SIG_SETMASK, &oldmask, 0)); +} diff --git a/test/posix/signal_test.c b/test/posix/signal_test.c new file mode 100644 index 000000000..bf5c4af6f --- /dev/null +++ b/test/posix/signal_test.c @@ -0,0 +1,55 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2023 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include +#include + +volatile sig_atomic_t signal_received = 0; + +void signal_handler(int signum) { + // Set the flag to indicate the signal was received + signal_received = 1; +} + +int main() { + // Install the signal handler + struct sigaction sa; + sa.sa_handler = signal_handler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + + if (sigaction(SIGUSR1, &sa, NULL) == -1) { + // Failed to install signal handler + exit(1); + } + + // Raise the signal + if (raise(SIGUSR1) != 0) { + // Failed to raise signal + exit(2); + } + + // Check if the signal was received + if (signal_received == 1) { + // Signal was successfully caught + exit(0); + } else { + // Signal was not caught + exit(3); + } +} diff --git a/test/posix/test.mk b/test/posix/test.mk new file mode 100644 index 000000000..89423f9d6 --- /dev/null +++ b/test/posix/test.mk @@ -0,0 +1,59 @@ +#-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐ +#───vi: set et ft=make ts=8 tw=8 fenc=utf-8 :vi───────────────────────┘ + +PKGS += TEST_POSIX + +TEST_POSIX_SRCS := \ + $(wildcard test/posix/*.c) + +TEST_POSIX_SRCS_TEST = \ + $(filter %_test.c,$(TEST_POSIX_SRCS)) + +TEST_POSIX_OBJS = \ + $(TEST_POSIX_SRCS:%.c=o/$(MODE)/%.o) + +TEST_POSIX_COMS = \ + $(TEST_POSIX_SRCS_TEST:%.c=o/$(MODE)/%.com) + +TEST_POSIX_BINS = \ + $(TEST_POSIX_COMS) \ + $(TEST_POSIX_COMS:%=%.dbg) + +TEST_POSIX_TESTS = \ + $(TEST_POSIX_SRCS_TEST:%.c=o/$(MODE)/%.com.ok) + +TEST_POSIX_CHECKS = \ + $(TEST_POSIX_SRCS_TEST:%.c=o/$(MODE)/%.com.runs) + +TEST_POSIX_DIRECTDEPS = \ + LIBC_CALLS \ + LIBC_FMT \ + LIBC_INTRIN \ + LIBC_MEM \ + LIBC_PROC \ + LIBC_RUNTIME \ + LIBC_STDIO \ + LIBC_SYSV \ + LIBC_THREAD + +TEST_POSIX_DEPS := \ + $(call uniq,$(foreach x,$(TEST_POSIX_DIRECTDEPS),$($(x)))) + +o/$(MODE)/test/posix/posix.pkg: \ + $(TEST_POSIX_OBJS) \ + $(foreach x,$(TEST_POSIX_DIRECTDEPS),$($(x)_A).pkg) + +o/$(MODE)/test/posix/%.com.dbg: \ + $(TEST_POSIX_DEPS) \ + o/$(MODE)/test/posix/%.o \ + o/$(MODE)/test/posix/posix.pkg \ + $(CRT) \ + $(APE_NO_MODIFY_SELF) + @$(APELINK) + +$(TEST_POSIX_OBJS): private CFLAGS += -isystem isystem/ + +.PHONY: o/$(MODE)/test/posix +o/$(MODE)/test/posix: \ + $(TEST_POSIX_BINS) \ + $(TEST_POSIX_CHECKS) diff --git a/test/test.mk b/test/test.mk index 02f1f4274..5a9af8161 100644 --- a/test/test.mk +++ b/test/test.mk @@ -5,4 +5,5 @@ o/$(MODE)/test: o/$(MODE)/test/dsp \ o/$(MODE)/test/libc \ o/$(MODE)/test/net \ + o/$(MODE)/test/posix \ o/$(MODE)/test/tool