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