1570af302Sopenharmony_ci// testing cancellation points
2570af302Sopenharmony_ci#include <errno.h>
3570af302Sopenharmony_ci#include <pthread.h>
4570af302Sopenharmony_ci#include <semaphore.h>
5570af302Sopenharmony_ci#include <string.h>
6570af302Sopenharmony_ci#include <sys/mman.h>
7570af302Sopenharmony_ci#include <fcntl.h>
8570af302Sopenharmony_ci#include <unistd.h>
9570af302Sopenharmony_ci#include "test.h"
10570af302Sopenharmony_ci
11570af302Sopenharmony_ci#define TESTC(c, m) ( (c) || (t_error(#c " failed (%s, " m ")\n", cdescr), 0) )
12570af302Sopenharmony_ci#define TESTR(f, m) do {int r; \
13570af302Sopenharmony_ci	if ((r = (f))) t_error(#f " failed: %s (%s, " m ")\n", strerror(r), cdescr); } while (0)
14570af302Sopenharmony_ci#define TESTE(f, m) do { \
15570af302Sopenharmony_ci	if ((f)==-1) t_error(#f " failed: %s (%s, " m ")\n", strerror(errno), cdescr); } while (0)
16570af302Sopenharmony_ci
17570af302Sopenharmony_cistatic sem_t sem_seq, sem_test;
18570af302Sopenharmony_ci
19570af302Sopenharmony_cistatic int seqno;
20570af302Sopenharmony_ci
21570af302Sopenharmony_cistatic const char *cdescr = "global initialization";
22570af302Sopenharmony_ci
23570af302Sopenharmony_cistatic void prepare_sem(void *arg)
24570af302Sopenharmony_ci{
25570af302Sopenharmony_ci	TESTR(sem_init(&sem_test, 0, (long)arg), "creating semaphore");
26570af302Sopenharmony_ci}
27570af302Sopenharmony_ci
28570af302Sopenharmony_cistatic void cleanup_sem(void *arg)
29570af302Sopenharmony_ci{
30570af302Sopenharmony_ci	TESTR(sem_destroy(&sem_test), "destroying semaphore");
31570af302Sopenharmony_ci}
32570af302Sopenharmony_ci
33570af302Sopenharmony_cistatic void execute_sem_wait(void *arg)
34570af302Sopenharmony_ci{
35570af302Sopenharmony_ci	TESTR(sem_wait(&sem_test), "waiting on semaphore in the canceled thread");
36570af302Sopenharmony_ci}
37570af302Sopenharmony_ci
38570af302Sopenharmony_cistatic void execute_sem_timedwait(void *arg)
39570af302Sopenharmony_ci{
40570af302Sopenharmony_ci	struct timespec ts;
41570af302Sopenharmony_ci	clock_gettime(CLOCK_REALTIME, &ts);
42570af302Sopenharmony_ci	ts.tv_sec += 1;
43570af302Sopenharmony_ci	TESTR(sem_timedwait(&sem_test, &ts), "timed-waiting on semaphore in the canceled thread");
44570af302Sopenharmony_ci}
45570af302Sopenharmony_ci
46570af302Sopenharmony_cistatic pthread_t td_test;
47570af302Sopenharmony_ci
48570af302Sopenharmony_cistatic void *run_test(void *arg)
49570af302Sopenharmony_ci{
50570af302Sopenharmony_ci	while (sem_wait(&sem_test));
51570af302Sopenharmony_ci	return 0;
52570af302Sopenharmony_ci}
53570af302Sopenharmony_ci
54570af302Sopenharmony_cistatic void prepare_thread(void *arg)
55570af302Sopenharmony_ci{
56570af302Sopenharmony_ci	prepare_sem(arg);
57570af302Sopenharmony_ci	TESTR(pthread_create(&td_test, 0, run_test, 0), "creating auxiliary thread");
58570af302Sopenharmony_ci}
59570af302Sopenharmony_ci
60570af302Sopenharmony_cistatic void cleanup_thread(void *arg)
61570af302Sopenharmony_ci{
62570af302Sopenharmony_ci	void *res;
63570af302Sopenharmony_ci	if (td_test) {
64570af302Sopenharmony_ci		TESTR(sem_post(&sem_test), "posting semaphore");
65570af302Sopenharmony_ci		TESTR(pthread_join(td_test, &res), "joining auxiliary thread");
66570af302Sopenharmony_ci		TESTC(res == 0, "auxiliary thread exit status");
67570af302Sopenharmony_ci	}
68570af302Sopenharmony_ci	cleanup_sem(arg);
69570af302Sopenharmony_ci}
70570af302Sopenharmony_ci
71570af302Sopenharmony_cistatic void execute_thread_join(void *arg)
72570af302Sopenharmony_ci{
73570af302Sopenharmony_ci	TESTR(pthread_join(td_test, 0), "joining in the canceled thread");
74570af302Sopenharmony_ci	td_test = 0;
75570af302Sopenharmony_ci}
76570af302Sopenharmony_ci
77570af302Sopenharmony_cistatic void prepare_dummy(void *arg)
78570af302Sopenharmony_ci{
79570af302Sopenharmony_ci}
80570af302Sopenharmony_ci
81570af302Sopenharmony_cistatic void execute_shm_open(void *arg)
82570af302Sopenharmony_ci{
83570af302Sopenharmony_ci	int *fd = arg;
84570af302Sopenharmony_ci	TESTE(*fd = shm_open("/testshm", O_RDWR|O_CREAT, 0666), "");
85570af302Sopenharmony_ci}
86570af302Sopenharmony_ci
87570af302Sopenharmony_cistatic void cleanup_shm(void *arg)
88570af302Sopenharmony_ci{
89570af302Sopenharmony_ci	int *fd = arg;
90570af302Sopenharmony_ci	if (*fd > 0) {
91570af302Sopenharmony_ci		TESTE(close(*fd), "shm fd");
92570af302Sopenharmony_ci		TESTE(shm_unlink("/testshm"), "");
93570af302Sopenharmony_ci	}
94570af302Sopenharmony_ci}
95570af302Sopenharmony_ci
96570af302Sopenharmony_cistatic struct {
97570af302Sopenharmony_ci	int want_cancel;
98570af302Sopenharmony_ci	void (*prepare)(void *);
99570af302Sopenharmony_ci	void (*execute)(void *);
100570af302Sopenharmony_ci	void (*cleanup)(void *);
101570af302Sopenharmony_ci	void *arg;
102570af302Sopenharmony_ci	const char *descr;
103570af302Sopenharmony_ci} scenarios[] = {
104570af302Sopenharmony_ci	{1, prepare_sem, execute_sem_wait, cleanup_sem, 0, "blocking sem_wait"},
105570af302Sopenharmony_ci	{1, prepare_sem, execute_sem_wait, cleanup_sem, (void*)1, "non-blocking sem_wait"},
106570af302Sopenharmony_ci	{1, prepare_sem, execute_sem_timedwait, cleanup_sem, 0, "blocking sem_timedwait"},
107570af302Sopenharmony_ci	{1, prepare_sem, execute_sem_timedwait, cleanup_sem, (void*)1, "non-blocking sem_timedwait"},
108570af302Sopenharmony_ci	{1, prepare_thread, execute_thread_join, cleanup_thread, 0, "blocking pthread_join"},
109570af302Sopenharmony_ci	{1, prepare_thread, execute_thread_join, cleanup_thread, (void*)1, "non-blocking pthread_join"},
110570af302Sopenharmony_ci	{0, prepare_dummy, execute_shm_open, cleanup_shm, &(int){0}, "shm_open"},
111570af302Sopenharmony_ci	{ 0 }
112570af302Sopenharmony_ci}, *cur_sc = scenarios;
113570af302Sopenharmony_ci
114570af302Sopenharmony_cistatic void *run_execute(void *arg)
115570af302Sopenharmony_ci{
116570af302Sopenharmony_ci	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, 0);
117570af302Sopenharmony_ci	while (sem_wait(&sem_seq));
118570af302Sopenharmony_ci	pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, 0);
119570af302Sopenharmony_ci	seqno = 1;
120570af302Sopenharmony_ci	cur_sc->execute(cur_sc->arg);
121570af302Sopenharmony_ci	seqno = 2;
122570af302Sopenharmony_ci	return 0;
123570af302Sopenharmony_ci}
124570af302Sopenharmony_ci
125570af302Sopenharmony_ciint main(void)
126570af302Sopenharmony_ci{
127570af302Sopenharmony_ci	TESTR(sem_init(&sem_seq, 0, 0), "creating semaphore");
128570af302Sopenharmony_ci
129570af302Sopenharmony_ci	for (; cur_sc->prepare; cur_sc++) {
130570af302Sopenharmony_ci		pthread_t td;
131570af302Sopenharmony_ci		void *res;
132570af302Sopenharmony_ci
133570af302Sopenharmony_ci		cdescr = cur_sc->descr;
134570af302Sopenharmony_ci		cur_sc->prepare(cur_sc->arg);
135570af302Sopenharmony_ci		seqno = 0;
136570af302Sopenharmony_ci		TESTR(pthread_create(&td, 0, run_execute, 0), "creating thread to be canceled");
137570af302Sopenharmony_ci		TESTR(pthread_cancel(td), "canceling");
138570af302Sopenharmony_ci		TESTR(sem_post(&sem_seq), "unblocking canceled thread");
139570af302Sopenharmony_ci		TESTR(pthread_join(td, &res), "joining canceled thread");
140570af302Sopenharmony_ci		if (cur_sc->want_cancel) {
141570af302Sopenharmony_ci			TESTC(res == PTHREAD_CANCELED, "canceled thread exit status")
142570af302Sopenharmony_ci			&& TESTC(seqno == 1, "seqno");
143570af302Sopenharmony_ci		} else {
144570af302Sopenharmony_ci			TESTC(res != PTHREAD_CANCELED, "canceled thread exit status")
145570af302Sopenharmony_ci			&& TESTC(seqno == 2, "seqno");
146570af302Sopenharmony_ci		}
147570af302Sopenharmony_ci		cur_sc->cleanup(cur_sc->arg);
148570af302Sopenharmony_ci	}
149570af302Sopenharmony_ci
150570af302Sopenharmony_ci	return t_status;
151570af302Sopenharmony_ci}
152