/*	$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 <sys/cdefs.h>
__RCSID("$NetBSD: t_cancellation.c,v 1.4 2025/04/05 11:22:32 riastradh Exp $");

#include <sys/event.h>
#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 "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, &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);
	}

	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();
}