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