/* $NetBSD: t_cancellation.c,v 1.4 2025/04/05 11:22:32 riastradh Exp $ */ /* * Copyright (c) 2025 The NetBSD Foundation, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include __RCSID("$NetBSD: t_cancellation.c,v 1.4 2025/04/05 11:22:32 riastradh Exp $"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "cancelpoint.h" #include "h_macros.h" static const char * c11thrd_err(int error) { static char buf[32]; switch (error) { case thrd_busy: return "thrd_busy"; case thrd_nomem: return "thrd_nomem"; case thrd_success: return "thrd_success"; case thrd_timedout: return "thrd_timedout"; default: snprintf(buf, sizeof(buf), "thrd_%d", error); return buf; } } #define RT(x) do \ { \ int RT_rv = (x); \ ATF_REQUIRE_MSG(RT_rv == 0, "%s: %d (%s)", \ #x, RT_rv, c11thrd_err(RT_rv)); \ } while (0) pthread_barrier_t bar; bool cleanup_done; /* POSIX style */ static void * emptythread(void *cookie) { return NULL; } /* C11 style */ static int emptythrd(void *cookie) { return 123; } static void cleanup_pthread_join(void *cookie) { pthread_t *tp = cookie; void *result; RZ(pthread_join(*tp, &result)); ATF_CHECK_MSG(result == NULL, "result=%p", result); } static void cleanup_thrd_join(void *cookie) { thrd_t *tp = cookie; int result; RT(thrd_join(*tp, &result)); ATF_CHECK_MSG(result == 123, "result=%d", result); } static void cleanup_msgid(void *cookie) { int *msgidp = cookie; /* * These message queue identifiers are persistent, so make sure * to clean them up; otherwise the operator will have to run * `ipcrm -q all' from time to time or else the tests will fail * with ENOSPC. */ RL(msgctl(*msgidp, IPC_RMID, NULL)); } /* * List of cancellation points in POSIX: * * https://pubs.opengroup.org/onlinepubs/9799919799.2024edition/functions/V2_chap02.html#tag_16_09_05_02 */ static int acceptsetup(void) { struct sockaddr_un sun = { .sun_family = AF_LOCAL }; int sock; strncpy(sun.sun_path, "sock", sizeof(sun.sun_path)); RL(sock = socket(PF_LOCAL, SOCK_STREAM, 0)); RL(bind(sock, (const struct sockaddr *)&sun, sizeof(sun))); RL(listen(sock, 1)); return sock; } static void cancelpoint_accept(void) { const int sock = acceptsetup(); cancelpointready(); RL(accept(sock, NULL, NULL)); } static void cancelpoint_accept4(void) { const int sock = acceptsetup(); cancelpointready(); RL(accept4(sock, NULL, NULL, O_CLOEXEC)); } static void cancelpoint_aio_suspend(void) { int fd[2]; char buf[32]; struct aiocb aio = { .aio_offset = 0, .aio_buf = buf, .aio_nbytes = sizeof(buf), .aio_fildes = -1, }; const struct aiocb *const aiolist[] = { &aio }; RL(pipe(fd)); aio.aio_fildes = fd[0]; RL(aio_read(&aio)); cancelpointready(); RL(aio_suspend(aiolist, __arraycount(aiolist), NULL)); } static void cancelpoint_clock_nanosleep(void) { /* XXX test all CLOCK_*? */ struct timespec t = {.tv_sec = 1, .tv_nsec = 0}; cancelpointready(); RL(clock_nanosleep(CLOCK_MONOTONIC, 0, &t, NULL)); } static void cancelpoint_close(void) { int fd; RL(fd = open("/dev/null", O_RDWR)); cancelpointready(); RL(close(fd)); } static void cancelpoint_cnd_timedwait(void) { cnd_t cnd; mtx_t mtx; struct timespec t = {.tv_sec = 1, .tv_nsec = 0}; RT(cnd_init(&cnd)); RT(mtx_init(&mtx, mtx_plain)); cancelpointready(); RT(mtx_lock(&mtx)); RT(cnd_timedwait(&cnd, &mtx, &t)); RT(mtx_unlock(&mtx)); } static void cancelpoint_cnd_wait(void) { cnd_t cnd; mtx_t mtx; RT(cnd_init(&cnd)); RT(mtx_init(&mtx, mtx_plain)); cancelpointready(); RT(mtx_lock(&mtx)); RT(cnd_wait(&cnd, &mtx)); RT(mtx_unlock(&mtx)); } static void cancelpoint_connect(void) { struct sockaddr_un sun = { .sun_family = AF_LOCAL }; int sock; strncpy(sun.sun_path, "sock", sizeof(sun.sun_path)); RL(sock = socket(PF_LOCAL, SOCK_STREAM, 0)); cancelpointready(); RL(connect(sock, (const struct sockaddr *)&sun, sizeof(sun))); } static void cancelpoint_creat(void) { cancelpointready(); RL(creat("file", 0666)); } static void cancelpoint_fcntl_F_SETLKW(void) { int fd; struct flock fl = { .l_start = 0, .l_len = 0, .l_type = F_WRLCK, .l_whence = SEEK_SET, }; RL(fd = open("file", O_RDWR|O_CREAT, 0666)); cancelpointready(); RL(fcntl(fd, F_SETLKW, &fl)); } static void cancelpoint_fcntl_F_OFD_SETLKW(void) { #ifdef F_OFD_SETLKW int fd; struct flock fl = { .l_start = 0, .l_len = 0, .l_type = F_WRLCK, .l_whence = SEEK_SET, }; RL(fd = open("file", O_RDWR|O_CREAT, 0666)); cancelpointready(); RL(fcntl(fd, F_OFD_SETLKW, &fl)); #else atf_tc_expect_fail("PR kern/59241: POSIX.1-2024:" " OFD-owned file locks"); atf_tc_fail("no F_OFD_SETLKW"); #endif } static void cancelpoint_fdatasync(void) { int fd; RL(fd = open("file", O_RDWR|O_CREAT, 0666)); cancelpointready(); RL(fdatasync(fd)); } static void cancelpoint_fsync(void) { int fd; RL(fd = open("file", O_RDWR|O_CREAT, 0666)); cancelpointready(); RL(fsync(fd)); } static void cancelpoint_kevent(void) { int kq; struct kevent ev; EV_SET(&ev, SIGUSR1, EVFILT_SIGNAL, EV_ADD|EV_ENABLE, /*fflags*/0, /*data*/0, /*udata*/0); RL(kq = kqueue()); RL(kevent(kq, &ev, 1, NULL, 1, &(const struct timespec){0,0})); cancelpointready(); RL(kevent(kq, NULL, 0, &ev, 1, NULL)); } static void cancelpoint_lockf_F_LOCK(void) { int fd; RL(fd = open("file", O_RDWR|O_CREAT, 0666)); cancelpointready(); RL(lockf(fd, F_LOCK, 0)); } static void cancelpoint_mq_receive(void) { mqd_t mq; char buf[32]; RL(mq = mq_open("mq", O_RDWR|O_CREAT, 0666, NULL)); cancelpointready(); RL(mq_receive(mq, buf, sizeof(buf), NULL)); } static void cancelpoint_mq_send(void) { mqd_t mq; char buf[32] = {0}; RL(mq = mq_open("mq", O_RDWR|O_CREAT, 0666, NULL)); cancelpointready(); RL(mq_send(mq, buf, sizeof(buf), 0)); } static void cancelpoint_mq_timedreceive(void) { mqd_t mq; char buf[32]; struct timespec t = {.tv_sec = 1, .tv_nsec = 0}; RL(mq = mq_open("mq", O_RDWR|O_CREAT, 0666, NULL)); cancelpointready(); RL(mq_timedreceive(mq, buf, sizeof(buf), NULL, &t)); } static void cancelpoint_mq_timedsend(void) { mqd_t mq; char buf[32] = {0}; struct timespec t = {.tv_sec = 1, .tv_nsec = 0}; RL(mq = mq_open("mq", O_RDWR|O_CREAT, 0666, NULL)); cancelpointready(); RL(mq_timedsend(mq, buf, sizeof(buf), 0, &t)); } static void cancelpoint_msgrcv(void) { int msgid; char buf[32]; RL(msgid = msgget(IPC_PRIVATE, IPC_CREAT)); pthread_cleanup_push(&cleanup_msgid, &msgid); cancelpointready(); RL(msgrcv(msgid, buf, sizeof(buf), 0, 0)); pthread_cleanup_pop(/*execute*/1); } static void cancelpoint_msgsnd(void) { int msgid; char buf[32] = {0}; RL(msgid = msgget(IPC_PRIVATE, IPC_CREAT)); pthread_cleanup_push(&cleanup_msgid, &msgid); cancelpointready(); RL(msgsnd(msgid, buf, sizeof(buf), 0)); pthread_cleanup_pop(/*execute*/1); } static void cancelpoint_msync(void) { const unsigned long pagesize = sysconf(_SC_PAGESIZE); int fd; void *map; RL(fd = open("file", O_RDWR|O_CREAT, 0666)); RL(ftruncate(fd, pagesize)); REQUIRE_LIBC(map = mmap(NULL, pagesize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0), MAP_FAILED); cancelpointready(); RL(msync(map, pagesize, MS_SYNC)); } static void cancelpoint_nanosleep(void) { /* XXX test all CLOCK_*? */ struct timespec t = {.tv_sec = 1, .tv_nsec = 0}; cancelpointready(); RL(nanosleep(&t, NULL)); } static void cancelpoint_open(void) { cancelpointready(); RL(open("file", O_RDWR)); } static void cancelpoint_openat(void) { cancelpointready(); RL(openat(AT_FDCWD, "file", O_RDWR)); } static void cancelpoint_pause(void) { cancelpointready(); RL(pause()); } static void cancelpoint_poll(void) { int fd[2]; struct pollfd pfd; RL(pipe(fd)); pfd.fd = fd[0]; pfd.events = POLLIN; cancelpointready(); RL(poll(&pfd, 1, 1000)); } static void cancelpoint_posix_close(void) { #if 0 int fd; RL(fd = open("file", O_RDWR|O_CREAT, 0666)); cancelpointready(); RL(posix_close(fd, POSIX_CLOSE_RESTART)); #else atf_tc_expect_fail("PR kern/58929: POSIX.1-2024 compliance:" " posix_close, POSIX_CLOSE_RESTART"); atf_tc_fail("no posix_close"); #endif } static void cancelpoint_ppoll(void) { int fd[2]; struct pollfd pfd; struct timespec t = {.tv_sec = 1, .tv_nsec = 0}; RL(pipe(fd)); pfd.fd = fd[0]; pfd.events = POLLIN; cancelpointready(); RL(ppoll(&pfd, 1, &t, NULL)); } static void cancelpoint_pread(void) { int fd; char buf[1]; RL(fd = open("file", O_RDWR|O_CREAT, 0666)); cancelpointready(); RL(pread(fd, buf, sizeof(buf), 1)); } static void cancelpoint_pselect(void) { int fd[2]; fd_set readfd; struct timespec t = {.tv_sec = 1, .tv_nsec = 0}; FD_ZERO(&readfd); RL(pipe(fd)); FD_SET(fd[0], &readfd); cancelpointready(); RL(pselect(fd[0] + 1, &readfd, NULL, NULL, &t, NULL)); } static void cancelpoint_pthread_cond_clockwait(void) { #if 0 pthread_cond_t cond; pthread_mutex_t mutex; struct timespec t = {.tv_sec = 1, .tv_nsec = 0}; RZ(pthread_cond_init(&cond, NULL)); RZ(pthread_mutex_init(&mutex, NULL)); cancelpointready(); RZ(pthread_mutex_lock(&mutex)); RZ(pthread_cond_clockwait(&cond, &mutex, CLOCK_MONOTONIC, &t)); RZ(pthread_mutex_unlock(&mutex)); #else atf_tc_expect_fail("PR lib/59142: POSIX.1-2024:" " pthread_cond_clockwait and company"); atf_tc_fail("no posix_cond_clockwait"); #endif } static void cancelpoint_pthread_cond_timedwait(void) { pthread_cond_t cond; pthread_mutex_t mutex; struct timespec t = {.tv_sec = 1, .tv_nsec = 0}; RZ(pthread_cond_init(&cond, NULL)); RZ(pthread_mutex_init(&mutex, NULL)); cancelpointready(); RZ(pthread_mutex_lock(&mutex)); RZ(pthread_cond_timedwait(&cond, &mutex, &t)); RZ(pthread_mutex_unlock(&mutex)); } static void cancelpoint_pthread_cond_wait(void) { pthread_cond_t cond; pthread_mutex_t mutex; RZ(pthread_cond_init(&cond, NULL)); RZ(pthread_mutex_init(&mutex, NULL)); cancelpointready(); RZ(pthread_mutex_lock(&mutex)); RZ(pthread_cond_wait(&cond, &mutex)); RZ(pthread_mutex_unlock(&mutex)); } static void cancelpoint_pthread_join(void) { pthread_t t; RZ(pthread_create(&t, NULL, &emptythread, NULL)); pthread_cleanup_push(&cleanup_pthread_join, &t); cancelpointready(); RZ(pthread_join(t, NULL)); pthread_cleanup_pop(/*execute*/0); } static void cancelpoint_pthread_testcancel(void) { cancelpointready(); pthread_testcancel(); } static void cancelpoint_pwrite(void) { int fd; char buf[1] = {0}; RL(fd = open("file", O_RDWR|O_CREAT, 0666)); cancelpointready(); RL(pwrite(fd, buf, sizeof(buf), 1)); } static void cancelpoint_read(void) { int fd; char buf[1]; RL(fd = open("file", O_RDWR|O_CREAT, 0666)); cancelpointready(); RL(read(fd, buf, sizeof(buf))); } static void cancelpoint_readv(void) { int fd; char buf[1]; struct iovec iov = { .iov_base = buf, .iov_len = sizeof(buf) }; RL(fd = open("file", O_RDWR|O_CREAT, 0666)); cancelpointready(); RL(readv(fd, &iov, 1)); } static void cancelpoint_recv(void) { struct sockaddr_un sun = { .sun_family = AF_LOCAL }; int sock; char buf[1]; strncpy(sun.sun_path, "sock", sizeof(sun.sun_path)); RL(sock = socket(PF_LOCAL, SOCK_DGRAM, 0)); RL(bind(sock, (const struct sockaddr *)&sun, sizeof(sun))); cancelpointready(); RL(recv(sock, buf, sizeof(buf), 0)); } static void cancelpoint_recvfrom(void) { struct sockaddr_un sun = { .sun_family = AF_LOCAL }; int sock; char buf[1]; struct sockaddr_storage ss; socklen_t len = sizeof(ss); strncpy(sun.sun_path, "sock", sizeof(sun.sun_path)); RL(sock = socket(PF_LOCAL, SOCK_DGRAM, 0)); RL(bind(sock, (const struct sockaddr *)&sun, sizeof(sun))); cancelpointready(); RL(recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *)&ss, &len)); } static void cancelpoint_recvmsg(void) { struct sockaddr_un sun = { .sun_family = AF_LOCAL }; int sock; char buf[1]; struct iovec iov = { .iov_base = buf, .iov_len = sizeof(buf) }; struct msghdr msg = { .msg_iov = &iov, .msg_iovlen = 1, }; strncpy(sun.sun_path, "sock", sizeof(sun.sun_path)); RL(sock = socket(PF_LOCAL, SOCK_DGRAM, 0)); RL(bind(sock, (const struct sockaddr *)&sun, sizeof(sun))); cancelpointready(); RL(recvmsg(sock, &msg, 0)); } static void cancelpoint_select(void) { int fd[2]; fd_set readfd; struct timeval t = {.tv_sec = 1, .tv_usec = 0}; FD_ZERO(&readfd); RL(pipe(fd)); FD_SET(fd[0], &readfd); cancelpointready(); RL(select(fd[0] + 1, &readfd, NULL, NULL, &t)); } static void cancelpoint_send(void) { struct sockaddr_un sun = { .sun_family = AF_LOCAL }; int sock; char buf[1] = {0}; strncpy(sun.sun_path, "sock", sizeof(sun.sun_path)); RL(sock = socket(PF_LOCAL, SOCK_DGRAM, 0)); RL(bind(sock, (const struct sockaddr *)&sun, sizeof(sun))); cancelpointready(); RL(send(sock, buf, sizeof(buf), 0)); } static void cancelpoint_sendto(void) { struct sockaddr_un sun = { .sun_family = AF_LOCAL }; int sock; char buf[1] = {0}; strncpy(sun.sun_path, "sock", sizeof(sun.sun_path)); RL(sock = socket(PF_LOCAL, SOCK_DGRAM, 0)); cancelpointready(); RL(sendto(sock, buf, sizeof(buf), 0, (const struct sockaddr *)&sun, sizeof(sun))); } static void cancelpoint_sendmsg(void) { struct sockaddr_un sun = { .sun_family = AF_LOCAL }; int sock; char buf[1] = {0}; struct iovec iov = { .iov_base = buf, .iov_len = sizeof(buf) }; struct msghdr msg = { .msg_name = (struct sockaddr *)&sun, .msg_namelen = sizeof(sun), .msg_iov = &iov, .msg_iovlen = 1, }; strncpy(sun.sun_path, "sock", sizeof(sun.sun_path)); RL(sock = socket(PF_LOCAL, SOCK_DGRAM, 0)); cancelpointready(); RL(sendmsg(sock, &msg, 0)); } static void cancelpoint_sigsuspend(void) { sigset_t mask, omask; RL(sigfillset(&mask)); RL(sigprocmask(SIG_BLOCK, &mask, &omask)); cancelpointready(); RL(sigsuspend(&omask)); } static void cancelpoint_sigtimedwait(void) { sigset_t mask, omask; siginfo_t info; struct timespec t = {.tv_sec = 1, .tv_nsec = 0}; RL(sigfillset(&mask)); RL(sigprocmask(SIG_BLOCK, &mask, &omask)); cancelpointready(); RL(sigtimedwait(&omask, &info, &t)); } static void cancelpoint_sigwait(void) { sigset_t mask, omask; int sig; RL(sigfillset(&mask)); RL(sigprocmask(SIG_BLOCK, &mask, &omask)); cancelpointready(); RL(sigwait(&omask, &sig)); } static void cancelpoint_sigwaitinfo(void) { sigset_t mask, omask; siginfo_t info; RL(sigfillset(&mask)); RL(sigprocmask(SIG_BLOCK, &mask, &omask)); cancelpointready(); RL(sigwaitinfo(&omask, &info)); } static void cancelpoint_sleep(void) { cancelpointready(); (void)sleep(1); } static void cancelpoint_tcdrain(void) { int hostfd, appfd; char *pts; RL(hostfd = posix_openpt(O_RDWR|O_NOCTTY)); RL(grantpt(hostfd)); RL(unlockpt(hostfd)); REQUIRE_LIBC(pts = ptsname(hostfd), NULL); RL(appfd = open(pts, O_RDWR|O_NOCTTY)); cancelpointready(); RL(tcdrain(appfd)); } static void cancelpoint_thrd_join(void) { thrd_t t; RT(thrd_create(&t, &emptythrd, NULL)); pthread_cleanup_push(&cleanup_thrd_join, &t); cancelpointready(); RT(thrd_join(t, NULL)); pthread_cleanup_pop(/*execute*/0); } static void cancelpoint_thrd_sleep(void) { struct timespec t = {.tv_sec = 1, .tv_nsec = 0}; cancelpointready(); RT(thrd_sleep(&t, NULL)); } static void cancelpoint_wait(void) { cancelpointready(); RL(wait(NULL)); } static void cancelpoint_waitid(void) { cancelpointready(); RL(waitid(P_ALL, 0, NULL, 0)); } static void cancelpoint_waitpid(void) { cancelpointready(); RL(waitpid(-1, NULL, 0)); } static void cancelpoint_write(void) { int fd; char buf[1] = {0}; RL(fd = open("file", O_RDWR|O_CREAT, 0666)); cancelpointready(); RL(write(fd, buf, sizeof(buf))); } static void cancelpoint_writev(void) { int fd; char buf[1] = {0}; struct iovec iov = { .iov_base = buf, .iov_len = sizeof(buf) }; RL(fd = open("file", O_RDWR|O_CREAT, 0666)); cancelpointready(); RL(writev(fd, &iov, 1)); } TEST_CANCELPOINT(cancelpoint_accept, __nothing) TEST_CANCELPOINT(cancelpoint_accept4, __nothing) TEST_CANCELPOINT(cancelpoint_aio_suspend, __nothing) TEST_CANCELPOINT(cancelpoint_clock_nanosleep, __nothing) TEST_CANCELPOINT(cancelpoint_close, __nothing) TEST_CANCELPOINT(cancelpoint_cnd_timedwait, __nothing) TEST_CANCELPOINT(cancelpoint_cnd_wait, __nothing) TEST_CANCELPOINT(cancelpoint_connect, __nothing) TEST_CANCELPOINT(cancelpoint_creat, __nothing) TEST_CANCELPOINT(cancelpoint_fcntl_F_SETLKW, __nothing) TEST_CANCELPOINT(cancelpoint_fcntl_F_OFD_SETLKW, __nothing) TEST_CANCELPOINT(cancelpoint_fdatasync, __nothing) TEST_CANCELPOINT(cancelpoint_fsync, __nothing) TEST_CANCELPOINT(cancelpoint_kevent, __nothing) TEST_CANCELPOINT(cancelpoint_lockf_F_LOCK, __nothing) TEST_CANCELPOINT(cancelpoint_mq_receive, __nothing) TEST_CANCELPOINT(cancelpoint_mq_send, __nothing) TEST_CANCELPOINT(cancelpoint_mq_timedreceive, __nothing) TEST_CANCELPOINT(cancelpoint_mq_timedsend, __nothing) TEST_CANCELPOINT(cancelpoint_msgrcv, __nothing) TEST_CANCELPOINT(cancelpoint_msgsnd, __nothing) TEST_CANCELPOINT(cancelpoint_msync, __nothing) TEST_CANCELPOINT(cancelpoint_nanosleep, __nothing) TEST_CANCELPOINT(cancelpoint_open, __nothing) TEST_CANCELPOINT(cancelpoint_openat, __nothing) TEST_CANCELPOINT(cancelpoint_pause, __nothing) TEST_CANCELPOINT(cancelpoint_poll, __nothing) TEST_CANCELPOINT(cancelpoint_posix_close, __nothing) TEST_CANCELPOINT(cancelpoint_ppoll, __nothing) TEST_CANCELPOINT(cancelpoint_pread, __nothing) TEST_CANCELPOINT(cancelpoint_pselect, __nothing) TEST_CANCELPOINT(cancelpoint_pthread_cond_clockwait, __nothing) TEST_CANCELPOINT(cancelpoint_pthread_cond_timedwait, __nothing) TEST_CANCELPOINT(cancelpoint_pthread_cond_wait, __nothing) TEST_CANCELPOINT(cancelpoint_pthread_join, __nothing) TEST_CANCELPOINT(cancelpoint_pthread_testcancel, __nothing) TEST_CANCELPOINT(cancelpoint_pwrite, __nothing) TEST_CANCELPOINT(cancelpoint_read, __nothing) TEST_CANCELPOINT(cancelpoint_readv, __nothing) TEST_CANCELPOINT(cancelpoint_recv, __nothing) TEST_CANCELPOINT(cancelpoint_recvfrom, __nothing) TEST_CANCELPOINT(cancelpoint_recvmsg, __nothing) TEST_CANCELPOINT(cancelpoint_select, __nothing) TEST_CANCELPOINT(cancelpoint_send, __nothing) TEST_CANCELPOINT(cancelpoint_sendto, __nothing) TEST_CANCELPOINT(cancelpoint_sendmsg, __nothing) TEST_CANCELPOINT(cancelpoint_sigsuspend, __nothing) TEST_CANCELPOINT(cancelpoint_sigtimedwait, __nothing) TEST_CANCELPOINT(cancelpoint_sigwait, __nothing) TEST_CANCELPOINT(cancelpoint_sigwaitinfo, __nothing) TEST_CANCELPOINT(cancelpoint_sleep, __nothing) TEST_CANCELPOINT(cancelpoint_tcdrain, __nothing) TEST_CANCELPOINT(cancelpoint_thrd_join, __nothing) TEST_CANCELPOINT(cancelpoint_thrd_sleep, __nothing) TEST_CANCELPOINT(cancelpoint_wait, __nothing) TEST_CANCELPOINT(cancelpoint_waitid, __nothing) TEST_CANCELPOINT(cancelpoint_waitpid, __nothing) TEST_CANCELPOINT(cancelpoint_write, __nothing) TEST_CANCELPOINT(cancelpoint_writev, __nothing) ATF_TC(cleanuppop0); ATF_TC_HEAD(cleanuppop0, tc) { atf_tc_set_md_var(tc, "descr", "Test pthread_cleanup_pop(0)"); } ATF_TC_BODY(cleanuppop0, tc) { pthread_cleanup_push(&cleanup, &cleanup_done); pthread_cleanup_pop(/*execute*/0); ATF_CHECK(!cleanup_done); } ATF_TC(cleanuppop1); ATF_TC_HEAD(cleanuppop1, tc) { atf_tc_set_md_var(tc, "descr", "Test pthread_cleanup_pop(1)"); } ATF_TC_BODY(cleanuppop1, tc) { pthread_cleanup_push(&cleanup, &cleanup_done); pthread_cleanup_pop(/*execute*/1); ATF_CHECK(cleanup_done); } static void * cancelself_async(void *cookie) { int *n = cookie; RZ(pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL)); pthread_cleanup_push(&cleanup, &cleanup_done); *n = 1; RZ(pthread_cancel(pthread_self())); /* cancel */ *n = 2; RZ(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL)); pthread_testcancel(); *n = 3; RZ(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL)); pthread_testcancel(); *n = 4; pthread_cleanup_pop(/*execute*/0); return NULL; } ATF_TC(cancelself_async); ATF_TC_HEAD(cancelself_async, tc) { atf_tc_set_md_var(tc, "descr", "Test pthread_cancel(pthread_self()) async"); } ATF_TC_BODY(cancelself_async, tc) { int n = 0; pthread_t t; RZ(pthread_create(&t, NULL, &cancelself_async, &n)); alarm(1); RZ(pthread_join(t, NULL)); atf_tc_expect_fail("lib/59135: PTHREAD_CANCEL_ASYNCHRONOUS" " doesn't do much"); ATF_CHECK_MSG(n == 1, "n=%d", n); atf_tc_expect_pass(); ATF_CHECK(cleanup_done); } static void * cancelself_deferred(void *cookie) { int *n = cookie; /* PTHREAD_CANCEL_DEFERRED by default */ pthread_cleanup_push(&cleanup, &cleanup_done); *n = 1; RZ(pthread_cancel(pthread_self())); *n = 2; RZ(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL)); *n = 3; pthread_testcancel(); *n = 4; RZ(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL)); *n = 5; pthread_testcancel(); /* cancel */ *n = 6; pthread_cleanup_pop(/*execute*/0); return NULL; } ATF_TC(cancelself_deferred); ATF_TC_HEAD(cancelself_deferred, tc) { atf_tc_set_md_var(tc, "descr", "Test pthread_cancel(pthread_self()) deferred"); } ATF_TC_BODY(cancelself_deferred, tc) { int n = 0; pthread_t t; RZ(pthread_create(&t, NULL, &cancelself_deferred, &n)); alarm(1); RZ(pthread_join(t, NULL)); ATF_CHECK_MSG(n == 5, "n=%d", n); ATF_CHECK(cleanup_done); } static void * defaults(void *cookie) { int state, type; fprintf(stderr, "created thread\n"); RZ(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &state)); RZ(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &type)); ATF_CHECK_MSG(state == PTHREAD_CANCEL_ENABLE, "state=%d PTHREAD_CANCEL_ENABLE=%d PTHREAD_CANCEL_DISABLE=%d", state, PTHREAD_CANCEL_ENABLE, PTHREAD_CANCEL_DISABLE); ATF_CHECK_MSG(type == PTHREAD_CANCEL_DEFERRED, "type=%d" " PTHREAD_CANCEL_DEFERRED=%d PTHREAD_CANCEL_ASYNCHRONOUS=%d", type, PTHREAD_CANCEL_DEFERRED, PTHREAD_CANCEL_ASYNCHRONOUS); return NULL; } ATF_TC(defaults); ATF_TC_HEAD(defaults, tc) { atf_tc_set_md_var(tc, "descr", "Test default cancelability"); } ATF_TC_BODY(defaults, tc) { pthread_t t; fprintf(stderr, "initial thread\n"); (void)defaults(NULL); RZ(pthread_create(&t, NULL, &defaults, NULL)); alarm(1); RZ(pthread_join(t, NULL)); } static void * disable_enable(void *cookie) { int *n = cookie; pthread_cleanup_push(&cleanup, &cleanup_done); *n = 1; pthread_testcancel(); *n = 2; RZ(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL)); *n = 3; (void)pthread_barrier_wait(&bar); *n = 4; pthread_testcancel(); *n = 5; (void)pthread_barrier_wait(&bar); *n = 6; pthread_testcancel(); *n = 7; RZ(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL)); *n = 8; pthread_testcancel(); /* cancel */ *n = 9; pthread_cleanup_pop(/*execute*/0); return NULL; } ATF_TC(disable_enable); ATF_TC_HEAD(disable_enable, tc) { atf_tc_set_md_var(tc, "descr", "Test disabling and re-enabling cancellation"); } ATF_TC_BODY(disable_enable, tc) { int n = 0; pthread_t t; RZ(pthread_barrier_init(&bar, NULL, 2)); RZ(pthread_create(&t, NULL, &disable_enable, &n)); (void)pthread_barrier_wait(&bar); RZ(pthread_cancel(t)); (void)pthread_barrier_wait(&bar); alarm(1); RZ(pthread_join(t, NULL)); ATF_CHECK_MSG(n == 8, "n=%d", n); ATF_CHECK(cleanup_done); } static void * notestcancel_loop_async(void *cookie) { RZ(pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL)); pthread_cleanup_push(&cleanup, &cleanup_done); (void)pthread_barrier_wait(&bar); for (;;) __insn_barrier(); pthread_cleanup_pop(/*execute*/0); return NULL; } ATF_TC(notestcancel_loop_async); ATF_TC_HEAD(notestcancel_loop_async, tc) { atf_tc_set_md_var(tc, "descr", "Test nothing in a loop with PTHREAD_CANCEL_ASYNCHRONOUS"); } ATF_TC_BODY(notestcancel_loop_async, tc) { pthread_t t; void *result; RZ(pthread_barrier_init(&bar, NULL, 2)); RZ(pthread_create(&t, NULL, ¬estcancel_loop_async, NULL)); (void)pthread_barrier_wait(&bar); RZ(pthread_cancel(t)); atf_tc_expect_signal(SIGALRM, "lib/59135: PTHREAD_CANCEL_ASYNCHRONOUS" " doesn't do much"); alarm(1); RZ(pthread_join(t, &result)); ATF_CHECK_MSG(result == PTHREAD_CANCELED, "result=%p PTHREAD_CANCELED=%p", result, PTHREAD_CANCELED); ATF_CHECK(cleanup_done); } static void * disable_enable_async(void *cookie) { int *n = cookie; pthread_cleanup_push(&cleanup, &cleanup_done); RZ(pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL)); *n = 1; pthread_testcancel(); *n = 2; RZ(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL)); *n = 3; (void)pthread_barrier_wait(&bar); *n = 4; pthread_testcancel(); *n = 5; (void)pthread_barrier_wait(&bar); *n = 6; pthread_testcancel(); *n = 7; RZ(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL)); /* cancel */ *n = 8; pthread_testcancel(); *n = 9; pthread_cleanup_pop(/*execute*/0); return NULL; } ATF_TC(disable_enable_async); ATF_TC_HEAD(disable_enable_async, tc) { atf_tc_set_md_var(tc, "descr", "Test disabling and re-enabling cancellation when asynchronous"); } ATF_TC_BODY(disable_enable_async, tc) { int n = 0; pthread_t t; RZ(pthread_barrier_init(&bar, NULL, 2)); RZ(pthread_create(&t, NULL, &disable_enable_async, &n)); (void)pthread_barrier_wait(&bar); RZ(pthread_cancel(t)); (void)pthread_barrier_wait(&bar); alarm(1); RZ(pthread_join(t, NULL)); ATF_CHECK_MSG(n == 7, "n=%d", n); ATF_CHECK(cleanup_done); } static void * disable_enable_setcanceltype_async(void *cookie) { int *n = cookie; pthread_cleanup_push(&cleanup, &cleanup_done); *n = 1; pthread_testcancel(); *n = 2; RZ(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL)); *n = 3; (void)pthread_barrier_wait(&bar); *n = 4; pthread_testcancel(); *n = 5; (void)pthread_barrier_wait(&bar); *n = 6; pthread_testcancel(); *n = 7; RZ(pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL)); *n = 8; pthread_testcancel(); *n = 9; RZ(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL)); /* cancel */ *n = 10; pthread_testcancel(); *n = 11; pthread_cleanup_pop(/*execute*/0); return NULL; } ATF_TC(disable_enable_setcanceltype_async); ATF_TC_HEAD(disable_enable_setcanceltype_async, tc) { atf_tc_set_md_var(tc, "descr", "Test disabling cancellation, setting it async, and re-enabling"); } ATF_TC_BODY(disable_enable_setcanceltype_async, tc) { int n = 0; pthread_t t; RZ(pthread_barrier_init(&bar, NULL, 2)); RZ(pthread_create(&t, NULL, &disable_enable_setcanceltype_async, &n)); (void)pthread_barrier_wait(&bar); RZ(pthread_cancel(t)); (void)pthread_barrier_wait(&bar); alarm(1); RZ(pthread_join(t, NULL)); ATF_CHECK_MSG(n == 9, "n=%d", n); ATF_CHECK(cleanup_done); } static void * setcanceltype_async(void *cookie) { int *n = cookie; pthread_cleanup_push(&cleanup, &cleanup_done); *n = 1; pthread_testcancel(); *n = 2; (void)pthread_barrier_wait(&bar); *n = 3; (void)pthread_barrier_wait(&bar); *n = 4; RZ(pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL)); /* cancel */ *n = 5; pthread_cleanup_pop(/*execute*/0); return NULL; } ATF_TC(setcanceltype_async); ATF_TC_HEAD(setcanceltype_async, tc) { atf_tc_set_md_var(tc, "descr", "Test disabling cancellation, setting it async, and re-enabling"); } ATF_TC_BODY(setcanceltype_async, tc) { int n = 0; pthread_t t; RZ(pthread_barrier_init(&bar, NULL, 2)); RZ(pthread_create(&t, NULL, &setcanceltype_async, &n)); (void)pthread_barrier_wait(&bar); RZ(pthread_cancel(t)); (void)pthread_barrier_wait(&bar); alarm(1); RZ(pthread_join(t, NULL)); ATF_CHECK_MSG(n == 4, "n=%d", n); ATF_CHECK(cleanup_done); } static void sighandler(int signo) { int state; RZ(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &state)); RZ(pthread_setcancelstate(state, NULL)); } static void * sigsafecancelstate(void *cookie) { atomic_ulong *n = cookie; char name[128]; pthread_cleanup_push(&cleanup, &cleanup_done); REQUIRE_LIBC(signal(SIGUSR1, &sighandler), SIG_ERR); (void)pthread_barrier_wait(&bar); while (atomic_load_explicit(n, memory_order_relaxed) != 0) { /* * Do some things that might take the same lock as * pthread_setcancelstate. */ RZ(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL)); RZ(pthread_getname_np(pthread_self(), name, sizeof(name))); RZ(pthread_setname_np(pthread_self(), "%s", name)); } pthread_cleanup_pop(/*execute*/1); return NULL; } ATF_TC(sigsafecancelstate); ATF_TC_HEAD(sigsafecancelstate, tc) { atf_tc_set_md_var(tc, "descr", "Test pthread_setcancelstate async-signal-safety"); } ATF_TC_BODY(sigsafecancelstate, tc) { pthread_t t; atomic_ulong n = 10000; void *result; RZ(pthread_barrier_init(&bar, NULL, 2)); RZ(pthread_create(&t, NULL, &sigsafecancelstate, &n)); (void)pthread_barrier_wait(&bar); while (atomic_load_explicit(&n, memory_order_relaxed)) { pthread_kill(t, SIGUSR1); atomic_store_explicit(&n, atomic_load_explicit(&n, memory_order_relaxed) - 1, memory_order_relaxed); } alarm(1); RZ(pthread_join(t, &result)); ATF_CHECK_MSG(result == NULL, "result=%p", result); ATF_CHECK(cleanup_done); } static void * testcancel_loop(void *cookie) { pthread_cleanup_push(&cleanup, &cleanup_done); (void)pthread_barrier_wait(&bar); for (;;) pthread_testcancel(); pthread_cleanup_pop(/*execute*/0); return NULL; } ATF_TC(testcancel_loop); ATF_TC_HEAD(testcancel_loop, tc) { atf_tc_set_md_var(tc, "descr", "Test pthread_testcancel in a loop"); } ATF_TC_BODY(testcancel_loop, tc) { pthread_t t; void *result; RZ(pthread_barrier_init(&bar, NULL, 2)); RZ(pthread_create(&t, NULL, &testcancel_loop, NULL)); (void)pthread_barrier_wait(&bar); RZ(pthread_cancel(t)); alarm(1); RZ(pthread_join(t, &result)); ATF_CHECK_MSG(result == PTHREAD_CANCELED, "result=%p PTHREAD_CANCELED=%p", result, PTHREAD_CANCELED); ATF_CHECK(cleanup_done); } ATF_TP_ADD_TCS(tp) { ADD_TEST_CANCELPOINT(cancelpoint_accept); ADD_TEST_CANCELPOINT(cancelpoint_accept4); ADD_TEST_CANCELPOINT(cancelpoint_aio_suspend); ADD_TEST_CANCELPOINT(cancelpoint_clock_nanosleep); ADD_TEST_CANCELPOINT(cancelpoint_close); ADD_TEST_CANCELPOINT(cancelpoint_cnd_timedwait); ADD_TEST_CANCELPOINT(cancelpoint_cnd_wait); ADD_TEST_CANCELPOINT(cancelpoint_connect); ADD_TEST_CANCELPOINT(cancelpoint_creat); ADD_TEST_CANCELPOINT(cancelpoint_fcntl_F_SETLKW); ADD_TEST_CANCELPOINT(cancelpoint_fcntl_F_OFD_SETLKW); ADD_TEST_CANCELPOINT(cancelpoint_fdatasync); ADD_TEST_CANCELPOINT(cancelpoint_fsync); ADD_TEST_CANCELPOINT(cancelpoint_kevent); ADD_TEST_CANCELPOINT(cancelpoint_lockf_F_LOCK); ADD_TEST_CANCELPOINT(cancelpoint_mq_receive); ADD_TEST_CANCELPOINT(cancelpoint_mq_send); ADD_TEST_CANCELPOINT(cancelpoint_mq_timedreceive); ADD_TEST_CANCELPOINT(cancelpoint_mq_timedsend); ADD_TEST_CANCELPOINT(cancelpoint_msgrcv); ADD_TEST_CANCELPOINT(cancelpoint_msgsnd); ADD_TEST_CANCELPOINT(cancelpoint_msync); ADD_TEST_CANCELPOINT(cancelpoint_nanosleep); ADD_TEST_CANCELPOINT(cancelpoint_open); ADD_TEST_CANCELPOINT(cancelpoint_openat); ADD_TEST_CANCELPOINT(cancelpoint_pause); ADD_TEST_CANCELPOINT(cancelpoint_poll); ADD_TEST_CANCELPOINT(cancelpoint_posix_close); ADD_TEST_CANCELPOINT(cancelpoint_ppoll); ADD_TEST_CANCELPOINT(cancelpoint_pread); ADD_TEST_CANCELPOINT(cancelpoint_pselect); ADD_TEST_CANCELPOINT(cancelpoint_pthread_cond_clockwait); ADD_TEST_CANCELPOINT(cancelpoint_pthread_cond_timedwait); ADD_TEST_CANCELPOINT(cancelpoint_pthread_cond_wait); ADD_TEST_CANCELPOINT(cancelpoint_pthread_join); ADD_TEST_CANCELPOINT(cancelpoint_pthread_testcancel); ADD_TEST_CANCELPOINT(cancelpoint_pwrite); ADD_TEST_CANCELPOINT(cancelpoint_read); ADD_TEST_CANCELPOINT(cancelpoint_readv); ADD_TEST_CANCELPOINT(cancelpoint_recv); ADD_TEST_CANCELPOINT(cancelpoint_recvfrom); ADD_TEST_CANCELPOINT(cancelpoint_recvmsg); ADD_TEST_CANCELPOINT(cancelpoint_select); ADD_TEST_CANCELPOINT(cancelpoint_send); ADD_TEST_CANCELPOINT(cancelpoint_sendto); ADD_TEST_CANCELPOINT(cancelpoint_sendmsg); ADD_TEST_CANCELPOINT(cancelpoint_sigsuspend); ADD_TEST_CANCELPOINT(cancelpoint_sigtimedwait); ADD_TEST_CANCELPOINT(cancelpoint_sigwait); ADD_TEST_CANCELPOINT(cancelpoint_sigwaitinfo); ADD_TEST_CANCELPOINT(cancelpoint_sleep); ADD_TEST_CANCELPOINT(cancelpoint_tcdrain); ADD_TEST_CANCELPOINT(cancelpoint_thrd_join); ADD_TEST_CANCELPOINT(cancelpoint_thrd_sleep); ADD_TEST_CANCELPOINT(cancelpoint_wait); ADD_TEST_CANCELPOINT(cancelpoint_waitid); ADD_TEST_CANCELPOINT(cancelpoint_waitpid); ADD_TEST_CANCELPOINT(cancelpoint_write); ADD_TEST_CANCELPOINT(cancelpoint_writev); ATF_TP_ADD_TC(tp, cleanuppop0); ATF_TP_ADD_TC(tp, cleanuppop1); ATF_TP_ADD_TC(tp, cancelself_async); ATF_TP_ADD_TC(tp, cancelself_deferred); ATF_TP_ADD_TC(tp, defaults); ATF_TP_ADD_TC(tp, disable_enable); ATF_TP_ADD_TC(tp, disable_enable_async); ATF_TP_ADD_TC(tp, disable_enable_setcanceltype_async); ATF_TP_ADD_TC(tp, setcanceltype_async); ATF_TP_ADD_TC(tp, notestcancel_loop_async); ATF_TP_ADD_TC(tp, sigsafecancelstate); ATF_TP_ADD_TC(tp, testcancel_loop); return atf_no_error(); }