xref: /openbsd/regress/lib/libpthread/cancel/cancel.c (revision 04e62e6e)
1 /*	$OpenBSD: cancel.c,v 1.9 2016/09/20 17:25:06 otto Exp $	*/
2 /* David Leonard <d@openbsd.org>, 1999. Public Domain. */
3 
4 #include <pthread.h>
5 #include <pthread_np.h>
6 #include <unistd.h>
7 #include <util.h>
8 #include <stdio.h>
9 #include <fcntl.h>
10 #include <stdlib.h>
11 #include "test.h"
12 
13 static pthread_cond_t cond;
14 static pthread_mutex_t mutex;
15 static struct timespec expiretime;
16 
17 static volatile int pv_state = 0;
18 
19 static void
p(void)20 p(void)
21 {
22 	CHECKr(pthread_mutex_lock(&mutex));
23 	if (pv_state <= 0) {
24 		CHECKr(pthread_cond_timedwait(&cond, &mutex, &expiretime));
25 	}
26 	pv_state--;
27 	CHECKr(pthread_mutex_unlock(&mutex));
28 }
29 
30 static void
v(void)31 v(void)
32 {
33 	int needsignal;
34 
35 	CHECKr(pthread_mutex_lock(&mutex));
36 	pv_state++;
37 	needsignal = (pv_state == 1);
38 	if (needsignal)
39 		CHECKr(pthread_cond_signal(&cond));
40 	CHECKr(pthread_mutex_unlock(&mutex));
41 }
42 
43 static void
c1handler(void * arg)44 c1handler(void *arg)
45 {
46 	CHECKe(close(*(int *)arg));
47 	v();
48 }
49 
50 static void *
child1fn(void * arg)51 child1fn(void *arg)
52 {
53 	int fd, dummy;
54 	char buf[1024];
55 	int len;
56 
57 	SET_NAME("c1");
58 	CHECKr(pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL));
59 	/* something that will block */
60 	CHECKe(openpty(&dummy, &fd, NULL, NULL, NULL));
61 	pthread_cleanup_push(c1handler, (void *)&fd);
62 	v();
63 	while (1) {
64 		CHECKe(len = read(fd, &buf, sizeof buf));
65 		printf("child 1 read %d bytes\n", len);
66 	}
67 	pthread_cleanup_pop(0);
68 	PANIC("child 1");
69 }
70 
71 static int c2_in_test = 0;
72 
73 static void
c2handler(void * arg)74 c2handler(void *arg)
75 {
76 	ASSERT(c2_in_test);
77 	v();
78 }
79 
80 static int message_seen = 0;
81 static void *
child2fn(void * arg)82 child2fn(void *arg)
83 {
84 	SET_NAME("c2");
85 
86 	CHECKr(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL));
87 	pthread_cleanup_push(c2handler, NULL);
88 	v();
89 
90 	while (1) {
91 		struct timespec now;
92 		struct timespec end;
93 
94 		/*
95 		 * XXX Be careful not to call any cancellation points
96 		 * until pthread_testcancel()
97 		 */
98 
99 		CHECKe(clock_gettime(CLOCK_REALTIME, &end));
100 		end.tv_sec ++;
101 
102 		while (1) {
103 			CHECKe(clock_gettime(CLOCK_REALTIME, &now));
104 			if (timespeccmp(&now, &end, >=))
105 				break;
106 			pthread_yield();
107 		}
108 
109 		/* XXX write() contains a cancellation point */
110 		/* printf("child 2 testing for cancel\n"); */
111 
112 		c2_in_test = 1;
113 		pthread_testcancel();
114 		printf("you should see this message exactly once\n");
115 		message_seen++;
116 		c2_in_test = 0;
117 		ASSERT(message_seen == 1);
118 		v();
119 	}
120 	pthread_cleanup_pop(0);
121 	PANIC("child 2");
122 }
123 
124 static int c3_cancel_survived;
125 
126 static void
c3handler(void * arg)127 c3handler(void *arg)
128 {
129 	printf("(fyi, cancellation of self %s instantaneous)\n",
130 		(c3_cancel_survived ? "was not" : "was"));
131 	v();
132 }
133 
134 static void *
child3fn(void * arg)135 child3fn(void *arg)
136 {
137 	SET_NAME("c3");
138 	pthread_cleanup_push(c3handler, NULL);
139 
140 	/* Cancel myself */
141 	CHECKr(pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL));
142 	c3_cancel_survived = 0;
143 	pthread_cancel(pthread_self());
144 	c3_cancel_survived = 1;
145 	pthread_testcancel();
146 	pthread_cleanup_pop(0);
147 
148 	PANIC("child 3");
149 }
150 
151 static int c4_cancel_early;
152 
153 static void
c4handler(void * arg)154 c4handler(void *arg)
155 {
156 	printf("early = %d\n", c4_cancel_early);
157 	ASSERT(c4_cancel_early == 0);
158 	v();
159 }
160 
161 static void *
child4fn(void * arg)162 child4fn(void *arg)
163 {
164 	SET_NAME("c4");
165 	pthread_cleanup_push(c4handler, NULL);
166 
167 	/* Cancel myself */
168 	CHECKr(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL));
169 
170 	c4_cancel_early = 3;
171 	pthread_cancel(pthread_self());
172 
173 	c4_cancel_early = 2;
174 	pthread_testcancel();
175 
176 	c4_cancel_early = 1;
177 	CHECKr(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL));
178 
179 	c4_cancel_early = 0;
180 	pthread_testcancel();
181 
182 	pthread_cleanup_pop(0);
183 
184 	PANIC("child 4");
185 }
186 
187 int
main(int argc,char * argv[])188 main(int argc, char *argv[])
189 {
190 	pthread_t child1, child2, child3, child4;
191 
192 	/* Set up our control flow */
193 	CHECKr(pthread_mutex_init(&mutex, NULL));
194 	CHECKr(pthread_cond_init(&cond, NULL));
195 	CHECKe(clock_gettime(CLOCK_REALTIME, &expiretime));
196 	expiretime.tv_sec += 5; /* this test shouldn't run over 5 seconds */
197 
198 	CHECKr(pthread_create(&child1, NULL, child1fn, NULL));
199 	CHECKr(pthread_create(&child2, NULL, child2fn, NULL));
200 	p();
201 	p();
202 
203 	CHECKr(pthread_cancel(child1));
204 	p();
205 
206 	/* Give thread 2 a chance to go through its deferred loop once */
207 	p();
208 	CHECKr(pthread_cancel(child2));
209 	p();
210 
211 	/* Child 3 cancels itself */
212 	CHECKr(pthread_create(&child3, NULL, child3fn, NULL));
213 	p();
214 
215 	/* Child 4 also cancels itself */
216 	CHECKr(pthread_create(&child4, NULL, child4fn, NULL));
217 	p();
218 
219 	/* Make sure they're all gone */
220 	CHECKr(pthread_join(child4, NULL));
221 	CHECKr(pthread_join(child3, NULL));
222 	CHECKr(pthread_join(child2, NULL));
223 	CHECKr(pthread_join(child1, NULL));
224 
225 	exit(0);
226 }
227