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, &notestcancel_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();
+}
+

Reply via email to