Module Name: src
Committed By: riastradh
Date: Mon Mar 31 13:57:06 UTC 2025
Modified Files:
src/distrib/sets/lists/debug: mi
src/distrib/sets/lists/tests: mi
src/tests/lib/libpthread: Makefile
Added Files:
src/tests/lib/libpthread: t_cancellation.c
Log Message:
pthread_cancel(3): Add some automatic tests.
PR lib/59240: POSIX.1-2024: cancellation point audit
PR lib/59134: POSIX-1.2024: pthread_setcancelstate must be
async-signal-safe
To generate a diff of this commit:
cvs rdiff -u -r1.470 -r1.471 src/distrib/sets/lists/debug/mi
cvs rdiff -u -r1.1362 -r1.1363 src/distrib/sets/lists/tests/mi
cvs rdiff -u -r1.16 -r1.17 src/tests/lib/libpthread/Makefile
cvs rdiff -u -r0 -r1.1 src/tests/lib/libpthread/t_cancellation.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/distrib/sets/lists/debug/mi
diff -u src/distrib/sets/lists/debug/mi:1.470 src/distrib/sets/lists/debug/mi:1.471
--- src/distrib/sets/lists/debug/mi:1.470 Fri Mar 28 18:41:55 2025
+++ src/distrib/sets/lists/debug/mi Mon Mar 31 13:57:06 2025
@@ -1,4 +1,4 @@
-# $NetBSD: mi,v 1.470 2025/03/28 18:41:55 riastradh Exp $
+# $NetBSD: mi,v 1.471 2025/03/31 13:57:06 riastradh Exp $
#
./etc/mtree/set.debug comp-sys-root
./usr/lib comp-sys-usr compatdir
@@ -2385,6 +2385,7 @@
./usr/libdata/debug/usr/tests/lib/libpthread/h_resolv.debug tests-lib-tests debug,atf,compattestfile
./usr/libdata/debug/usr/tests/lib/libpthread/h_thread_local_dtor.debug tests-lib-tests debug,atf,compattestfile
./usr/libdata/debug/usr/tests/lib/libpthread/t_barrier.debug tests-lib-tests debug,atf,compattestfile
+./usr/libdata/debug/usr/tests/lib/libpthread/t_cancellation.debug tests-lib-tests debug,atf,compattestfile
./usr/libdata/debug/usr/tests/lib/libpthread/t_call_once.debug tests-lib-tests debug,atf,compattestfile
./usr/libdata/debug/usr/tests/lib/libpthread/t_cnd.debug tests-lib-tests debug,atf,compattestfile
./usr/libdata/debug/usr/tests/lib/libpthread/t_cond.debug tests-lib-tests debug,atf,compattestfile
Index: src/distrib/sets/lists/tests/mi
diff -u src/distrib/sets/lists/tests/mi:1.1362 src/distrib/sets/lists/tests/mi:1.1363
--- src/distrib/sets/lists/tests/mi:1.1362 Fri Mar 28 18:41:55 2025
+++ src/distrib/sets/lists/tests/mi Mon Mar 31 13:57:06 2025
@@ -1,4 +1,4 @@
-# $NetBSD: mi,v 1.1362 2025/03/28 18:41:55 riastradh Exp $
+# $NetBSD: mi,v 1.1363 2025/03/31 13:57:06 riastradh Exp $
#
# Note: don't delete entries from here - mark them as "obsolete" instead.
#
@@ -4077,6 +4077,7 @@
./usr/tests/lib/libpthread/t_barrier tests-lib-tests compattestfile,atf
./usr/tests/lib/libpthread/t_call_once tests-lib-tests compattestfile,atf
./usr/tests/lib/libpthread/t_cancel tests-lib-tests compattestfile,atf
+./usr/tests/lib/libpthread/t_cancellation tests-lib-tests compattestfile,atf
./usr/tests/lib/libpthread/t_cnd tests-lib-tests compattestfile,atf
./usr/tests/lib/libpthread/t_cond tests-lib-tests compattestfile,atf
./usr/tests/lib/libpthread/t_condwait tests-lib-tests compattestfile,atf
Index: src/tests/lib/libpthread/Makefile
diff -u src/tests/lib/libpthread/Makefile:1.16 src/tests/lib/libpthread/Makefile:1.17
--- src/tests/lib/libpthread/Makefile:1.16 Fri Nov 24 16:21:17 2023
+++ src/tests/lib/libpthread/Makefile Mon Mar 31 13:57:06 2025
@@ -1,4 +1,4 @@
-# $NetBSD: Makefile,v 1.16 2023/11/24 16:21:17 riastradh Exp $
+# $NetBSD: Makefile,v 1.17 2025/03/31 13:57:06 riastradh Exp $
NOMAN= # defined
@@ -17,6 +17,7 @@ CPPFLAGS.t_condwait.c+= -I${.CURDIR}/../
TESTS_SH+= t_atexit
TESTS_C+= t_barrier
TESTS_SH+= t_cancel
+TESTS_C+= t_cancellation
TESTS_C+= t_cond
TESTS_C+= t_condwait
TESTS_C+= t_detach
@@ -43,6 +44,7 @@ TESTS_SH+= t_thread_local_dtor
TESTS_C+= t_timedmutex
LDADD.t_sem+= -lrt
+LDADD.t_cancellation+= -lrt
BINDIR= ${TESTSDIR}
PROGS= h_atexit
Added files:
Index: src/tests/lib/libpthread/t_cancellation.c
diff -u /dev/null src/tests/lib/libpthread/t_cancellation.c:1.1
--- /dev/null Mon Mar 31 13:57:07 2025
+++ src/tests/lib/libpthread/t_cancellation.c Mon Mar 31 13:57:06 2025
@@ -0,0 +1,1622 @@
+/* $NetBSD: t_cancellation.c,v 1.1 2025/03/31 13:57:06 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 <sys/cdefs.h>
+__RCSID("$NetBSD: t_cancellation.c,v 1.1 2025/03/31 13:57:06 riastradh Exp $");
+
+#include <sys/mman.h>
+#include <sys/msg.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/wait.h>
+
+#include <aio.h>
+#include <atf-c.h>
+#include <fcntl.h>
+#include <mqueue.h>
+#include <paths.h>
+#include <poll.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdatomic.h>
+#include <string.h>
+#include <termios.h>
+#include <threads.h>
+#include <time.h>
+#include <unistd.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;
+
+static void
+cleanup(void *cookie)
+{
+ bool *cleanup_donep = cookie;
+
+ *cleanup_donep = true;
+}
+
+/* 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
+ */
+
+#if 0
+atomic_bool cancelpointreadydone;
+#endif
+
+static void
+cancelpointready(void)
+{
+
+ (void)pthread_barrier_wait(&bar);
+ RL(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL));
+#if 0
+ atomic_store_release(&cancelpointreadydone, true);
+#endif
+}
+
+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_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));
+}
+
+static void *
+thread_cancelpoint(void *cookie)
+{
+ void (*cancelpoint)(void) = cookie;
+
+ RL(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL));
+ (void)pthread_barrier_wait(&bar);
+
+ pthread_cleanup_push(&cleanup, &cleanup_done);
+ (*cancelpoint)();
+ pthread_cleanup_pop(/*execute*/0);
+
+ return NULL;
+}
+
+static void
+test_cancelpoint_before(void (*cancelpoint)(void))
+{
+ pthread_t t;
+ void *result;
+
+ RZ(pthread_barrier_init(&bar, NULL, 2));
+ RZ(pthread_create(&t, NULL, &thread_cancelpoint, cancelpoint));
+
+ (void)pthread_barrier_wait(&bar);
+ fprintf(stderr, "cancel\n");
+ RZ(pthread_cancel(t));
+ (void)pthread_barrier_wait(&bar);
+
+ 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);
+}
+
+#if 0
+static void
+test_cancelpoint_wakeup(void (*cancelpoint)(void))
+{
+ pthread_t t;
+
+ RZ(pthread_barrier_init(&bar, NULL, 2));
+ RZ(pthread_create(&t, NULL, &cancelpoint_thread, cancelpoint));
+
+ (void)pthread_barrier_wait(&bar);
+ while (!atomic_load_acquire(&cancelpointreadydone))
+ continue;
+ while (!pthread_sleeping(t)) /* XXX find a way to do this */
+ continue;
+ RZ(pthread_cancel(t));
+}
+#endif
+
+#define TEST_CANCELPOINT(CANCELPOINT, XFAIL) \
+ATF_TC(CANCELPOINT); \
+ATF_TC_HEAD(CANCELPOINT, tc) \
+{ \
+ atf_tc_set_md_var(tc, "descr", "Test cancellation point: " \
+ #CANCELPOINT); \
+} \
+ATF_TC_BODY(CANCELPOINT, tc) \
+{ \
+ XFAIL; \
+ test_cancelpoint_before(&CANCELPOINT); \
+}
+#define ADD_TEST_CANCELPOINT(CANCELPOINT) \
+ ATF_TP_ADD_TC(tp, CANCELPOINT)
+
+TEST_CANCELPOINT(cancelpoint_accept, __nothing)
+TEST_CANCELPOINT(cancelpoint_accept4,
+ atf_tc_expect_signal(SIGALRM,
+ "PR lib/59240: POSIX.1-2024: cancellation point audit"))
+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_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,
+ atf_tc_expect_fail("PR lib/59240: POSIX.1-2024: cancellation point audit"))
+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);
+ }
+
+ atf_tc_expect_signal(SIGALRM, "PR lib/59134: POSIX-1.2024:"
+ " pthread_setcancelstate must be async-signal-safe");
+ 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_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();
+}
+