diff --git a/lib/libc/tests/stdlib/Makefile b/lib/libc/tests/stdlib/Makefile index 29cf895006d1..6f57b8014a1e 100644 --- a/lib/libc/tests/stdlib/Makefile +++ b/lib/libc/tests/stdlib/Makefile @@ -1,82 +1,83 @@ .include ATF_TESTS_C+= clearenv_test ATF_TESTS_C+= dynthr_test ATF_TESTS_C+= heapsort_test ATF_TESTS_C+= libc_exit_test ATF_TESTS_C+= mergesort_test ATF_TESTS_C+= qsort_test .if ${COMPILER_TYPE} == "clang" ATF_TESTS_C+= qsort_b_test .endif ATF_TESTS_C+= qsort_r_compat_test ATF_TESTS_C+= qsort_r_test ATF_TESTS_C+= qsort_s_test ATF_TESTS_C+= set_constraint_handler_s_test ATF_TESTS_C+= strfmon_test ATF_TESTS_C+= tsearch_test ATF_TESTS_CXX+= cxa_thread_atexit_test ATF_TESTS_CXX+= cxa_thread_atexit_nothr_test # All architectures on FreeBSD have fenv.h CFLAGS+= -D__HAVE_FENV # Define __HAVE_LONG_DOUBLE for architectures whose long double has greater # precision than their double. .if ${MACHINE_CPUARCH} == "aarch64" || \ ${MACHINE_CPUARCH} == "amd64" || \ ${MACHINE_CPUARCH} == "i386" || \ ${MACHINE_CPUARCH} == "riscv" CFLAGS+= -D__HAVE_LONG_DOUBLE .endif # TODO: t_getenv_thread, t_mi_vector_hash, t_strtoi NETBSD_ATF_TESTS_C+= abs_test NETBSD_ATF_TESTS_C+= atoi_test NETBSD_ATF_TESTS_C+= div_test NETBSD_ATF_TESTS_C+= getenv_test NETBSD_ATF_TESTS_C+= exit_test NETBSD_ATF_TESTS_C+= hsearch_test NETBSD_ATF_TESTS_C+= posix_memalign_test NETBSD_ATF_TESTS_C+= random_test NETBSD_ATF_TESTS_C+= strtod_test NETBSD_ATF_TESTS_C+= strtol_test NETBSD_ATF_TESTS_C+= system_test # TODO: need to come up with a correct explanation of what the patch pho does # with h_atexit #ATF_TESTS_SH= atexit_test NETBSD_ATF_TESTS_SH= getopt_test .include "../Makefile.netbsd-tests" BINDIR= ${TESTSDIR} # TODO: see comment above #PROGS+= h_atexit PROGS+= h_getopt h_getopt_long CFLAGS+= -I${.CURDIR} CXXSTD.cxa_thread_atexit_test= c++11 CXXSTD.cxa_thread_atexit_nothr_test= c++11 LIBADD.cxa_thread_atexit_test+= pthread # Tests that requires Blocks feature .for t in qsort_b_test CFLAGS.${t}.c+= -fblocks LIBADD.${t}+= BlocksRuntime .endfor .for t in h_getopt h_getopt_long CFLAGS.$t+= -I${LIBNETBSD_SRCDIR} -I${SRCTOP}/contrib/netbsd-tests LDFLAGS.$t+= -L${LIBNETBSD_OBJDIR} LIBADD.${t}+= netbsd util .endfor +LIBADD.libc_exit_test+= pthread LIBADD.strtod_test+= m SUBDIR+= dynthr_mod .include diff --git a/lib/libc/tests/stdlib/libc_exit_test.c b/lib/libc/tests/stdlib/libc_exit_test.c index c47d03d1d598..12965261bdb3 100644 --- a/lib/libc/tests/stdlib/libc_exit_test.c +++ b/lib/libc/tests/stdlib/libc_exit_test.c @@ -1,85 +1,154 @@ /*- * Copyright (c) 2023 Klara, Inc. * * SPDX-License-Identifier: BSD-2-Clause */ #include +#include #include #include #include #include static void func_a(void) { if (write(STDOUT_FILENO, "a", 1) != 1) _Exit(1); } static void func_b(void) { if (write(STDOUT_FILENO, "b", 1) != 1) _Exit(1); } static void func_c(void) { if (write(STDOUT_FILENO, "c", 1) != 1) _Exit(1); } static void child(void) { /* this will be received by the parent */ printf("hello, "); fflush(stdout); /* this won't, because quick_exit() does not flush */ printf("world"); /* these will be called in reverse order, producing "abc" */ if (at_quick_exit(func_c) != 0 || at_quick_exit(func_b) != 0 || at_quick_exit(func_a) != 0) _Exit(1); quick_exit(0); } ATF_TC_WITHOUT_HEAD(quick_exit); ATF_TC_BODY(quick_exit, tc) { char buf[100] = ""; ssize_t len; int p[2], wstatus = 0; pid_t pid; ATF_REQUIRE(pipe(p) == 0); pid = fork(); if (pid == 0) { if (dup2(p[1], STDOUT_FILENO) < 0) _Exit(1); (void)close(p[1]); (void)close(p[0]); child(); _Exit(1); } ATF_REQUIRE_MSG(pid > 0, "expect fork() to succeed"); ATF_CHECK_EQ_MSG(pid, waitpid(pid, &wstatus, 0), "expect to collect child process"); ATF_CHECK_EQ_MSG(0, wstatus, "expect child to exit cleanly"); ATF_CHECK_MSG((len = read(p[0], buf, sizeof(buf))) > 0, "expect to receive output from child"); ATF_CHECK_STREQ("hello, abc", buf); } +static void +myatexit1(void) +{ + exit(12); +} + +ATF_TC_WITHOUT_HEAD(recursive_exit1); +ATF_TC_BODY(recursive_exit1, tc) +{ + pid_t pid; + int wstatus; + + pid = fork(); + if (pid == 0) { + atexit(myatexit1); + exit(1); + } + ATF_REQUIRE_MSG(pid > 0, + "expect fork() to succeed"); + ATF_CHECK_EQ_MSG(pid, waitpid(pid, &wstatus, 0), + "expect to collect child process"); + ATF_CHECK(WIFEXITED(wstatus)); + ATF_CHECK_EQ(WEXITSTATUS(wstatus), 12); +} + +static pthread_barrier_t barrier; + +static void +myatexit2(void) +{ + pthread_barrier_wait(&barrier); + exit(12); +} + +static void * +mythreadexit(void *arg) +{ + pthread_barrier_wait(&barrier); + exit(15); +} + +ATF_TC_WITHOUT_HEAD(recursive_exit2); +ATF_TC_BODY(recursive_exit2, tc) +{ + pid_t pid; + int wstatus; + + pid = fork(); + if (pid == 0) { + pthread_t thr; + + atexit(myatexit2); + + pthread_barrier_init(&barrier, NULL, 2); + pthread_create(&thr, NULL, mythreadexit, NULL); + + exit(1); + } + ATF_REQUIRE_MSG(pid > 0, + "expect fork() to succeed"); + ATF_CHECK_EQ_MSG(pid, waitpid(pid, &wstatus, 0), + "expect to collect child process"); + ATF_CHECK(WIFEXITED(wstatus)); + ATF_CHECK_EQ(WEXITSTATUS(wstatus), 12); +} + ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, quick_exit); + ATF_TP_ADD_TC(tp, recursive_exit1); + ATF_TP_ADD_TC(tp, recursive_exit2); return (atf_no_error()); }