1 /* Copyright (C) 2003-2022 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3
4 The GNU C Library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) any later version.
8
9 The GNU C Library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public
15 License along with the GNU C Library; if not, see
16 <https://www.gnu.org/licenses/>. */
17
18 #include <aio.h>
19 #include <errno.h>
20 #include <pthread.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
25
26
27 static pthread_barrier_t b;
28
29
30 /* Cleanup handling test. */
31 static int cl_called;
32
33 static void
cl(void * arg)34 cl (void *arg)
35 {
36 ++cl_called;
37 }
38
39
40 static void *
tf(void * arg)41 tf (void *arg)
42 {
43 int r = pthread_barrier_wait (&b);
44 if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD)
45 {
46 puts ("tf: barrier_wait failed");
47 exit (1);
48 }
49
50 pthread_cleanup_push (cl, NULL);
51
52 const struct aiocb *l[1] = { arg };
53
54 TEMP_FAILURE_RETRY (aio_suspend (l, 1, NULL));
55
56 pthread_cleanup_pop (0);
57
58 puts ("tf: aio_suspend returned");
59
60 exit (1);
61 }
62
63
64 static void *
tf2(void * arg)65 tf2 (void *arg)
66 {
67 int r = pthread_barrier_wait (&b);
68 if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD)
69 {
70 puts ("tf2: barrier_wait failed");
71 exit (1);
72 }
73
74 pthread_cleanup_push (cl, NULL);
75
76 const struct aiocb *l[1] = { arg };
77 struct timespec ts = { .tv_sec = 1000, .tv_nsec = 0 };
78
79 TEMP_FAILURE_RETRY (aio_suspend (l, 1, &ts));
80
81 pthread_cleanup_pop (0);
82
83 puts ("tf2: aio_suspend returned");
84
85 exit (1);
86 }
87
88
89 static int
do_test(void)90 do_test (void)
91 {
92 int fds[2];
93 if (pipe (fds) != 0)
94 {
95 puts ("pipe failed");
96 return 1;
97 }
98
99 struct aiocb a, a2, *ap;
100 char mem[1];
101 memset (&a, '\0', sizeof (a));
102 a.aio_fildes = fds[0];
103 a.aio_buf = mem;
104 a.aio_nbytes = sizeof (mem);
105 if (aio_read (&a) != 0)
106 {
107 puts ("aio_read failed");
108 return 1;
109 }
110
111 if (pthread_barrier_init (&b, NULL, 2) != 0)
112 {
113 puts ("barrier_init failed");
114 return 1;
115 }
116
117 pthread_t th;
118 if (pthread_create (&th, NULL, tf, &a) != 0)
119 {
120 puts ("1st create failed");
121 return 1;
122 }
123
124 int r = pthread_barrier_wait (&b);
125 if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD)
126 {
127 puts ("barrier_wait failed");
128 exit (1);
129 }
130
131 struct timespec ts = { .tv_sec = 0, .tv_nsec = 100000000 };
132 while (nanosleep (&ts, &ts) != 0)
133 continue;
134
135 puts ("going to cancel tf in-time");
136 if (pthread_cancel (th) != 0)
137 {
138 puts ("1st cancel failed");
139 return 1;
140 }
141
142 void *status;
143 if (pthread_join (th, &status) != 0)
144 {
145 puts ("1st join failed");
146 return 1;
147 }
148 if (status != PTHREAD_CANCELED)
149 {
150 puts ("1st thread not canceled");
151 return 1;
152 }
153
154 if (cl_called == 0)
155 {
156 puts ("tf cleanup handler not called");
157 return 1;
158 }
159 if (cl_called > 1)
160 {
161 puts ("tf cleanup handler called more than once");
162 return 1;
163 }
164
165 cl_called = 0;
166
167 if (pthread_create (&th, NULL, tf2, &a) != 0)
168 {
169 puts ("2nd create failed");
170 return 1;
171 }
172
173 r = pthread_barrier_wait (&b);
174 if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD)
175 {
176 puts ("2nd barrier_wait failed");
177 exit (1);
178 }
179
180 ts.tv_sec = 0;
181 ts.tv_nsec = 100000000;
182 while (nanosleep (&ts, &ts) != 0)
183 continue;
184
185 puts ("going to cancel tf2 in-time");
186 if (pthread_cancel (th) != 0)
187 {
188 puts ("2nd cancel failed");
189 return 1;
190 }
191
192 if (pthread_join (th, &status) != 0)
193 {
194 puts ("2nd join failed");
195 return 1;
196 }
197 if (status != PTHREAD_CANCELED)
198 {
199 puts ("2nd thread not canceled");
200 return 1;
201 }
202
203 if (cl_called == 0)
204 {
205 puts ("tf2 cleanup handler not called");
206 return 1;
207 }
208 if (cl_called > 1)
209 {
210 puts ("tf2 cleanup handler called more than once");
211 return 1;
212 }
213
214 puts ("in-time cancellation succeeded");
215
216 ap = &a;
217 if (aio_cancel (fds[0], &a) != AIO_CANCELED)
218 {
219 puts ("aio_cancel failed");
220 /* If aio_cancel failed, we cannot reuse aiocb a. */
221 ap = &a2;
222 }
223
224
225 cl_called = 0;
226
227 size_t len2 = fpathconf (fds[1], _PC_PIPE_BUF);
228 size_t page_size = sysconf (_SC_PAGESIZE);
229 len2 = 20 * (len2 < page_size ? page_size : len2) + sizeof (mem) + 1;
230 char *mem2 = malloc (len2);
231 if (mem2 == NULL)
232 {
233 puts ("could not allocate memory for pipe write");
234 return 1;
235 }
236
237 memset (ap, '\0', sizeof (*ap));
238 ap->aio_fildes = fds[1];
239 ap->aio_buf = mem2;
240 ap->aio_nbytes = len2;
241 if (aio_write (ap) != 0)
242 {
243 puts ("aio_write failed");
244 return 1;
245 }
246
247 if (pthread_create (&th, NULL, tf, ap) != 0)
248 {
249 puts ("3rd create failed");
250 return 1;
251 }
252
253 puts ("going to cancel tf early");
254 if (pthread_cancel (th) != 0)
255 {
256 puts ("3rd cancel failed");
257 return 1;
258 }
259
260 r = pthread_barrier_wait (&b);
261 if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD)
262 {
263 puts ("3rd barrier_wait failed");
264 exit (1);
265 }
266
267 if (pthread_join (th, &status) != 0)
268 {
269 puts ("3rd join failed");
270 return 1;
271 }
272 if (status != PTHREAD_CANCELED)
273 {
274 puts ("3rd thread not canceled");
275 return 1;
276 }
277
278 if (cl_called == 0)
279 {
280 puts ("tf cleanup handler not called");
281 return 1;
282 }
283 if (cl_called > 1)
284 {
285 puts ("tf cleanup handler called more than once");
286 return 1;
287 }
288
289 cl_called = 0;
290
291 if (pthread_create (&th, NULL, tf2, ap) != 0)
292 {
293 puts ("4th create failed");
294 return 1;
295 }
296
297 puts ("going to cancel tf2 early");
298 if (pthread_cancel (th) != 0)
299 {
300 puts ("4th cancel failed");
301 return 1;
302 }
303
304 r = pthread_barrier_wait (&b);
305 if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD)
306 {
307 puts ("4th barrier_wait failed");
308 exit (1);
309 }
310
311 if (pthread_join (th, &status) != 0)
312 {
313 puts ("4th join failed");
314 return 1;
315 }
316 if (status != PTHREAD_CANCELED)
317 {
318 puts ("4th thread not canceled");
319 return 1;
320 }
321
322 if (cl_called == 0)
323 {
324 puts ("tf2 cleanup handler not called");
325 return 1;
326 }
327 if (cl_called > 1)
328 {
329 puts ("tf2 cleanup handler called more than once");
330 return 1;
331 }
332
333 puts ("early cancellation succeeded");
334
335 if (ap == &a2)
336 {
337 /* The aio_read(&a) was not canceled because the read request was
338 already in progress. In the meanwhile aio_write(ap) wrote something
339 to the pipe and the read request either has already been finished or
340 is able to read the requested byte.
341 Wait for the read request before returning from this function because
342 the return value and error code from the read syscall will be written
343 to the struct aiocb a, which lies on the stack of this function.
344 Otherwise the stack from subsequent function calls - e.g. _dl_fini -
345 will be corrupted, which can lead to undefined behaviour like a
346 segmentation fault. */
347 const struct aiocb *l[1] = { &a };
348 TEMP_FAILURE_RETRY (aio_suspend(l, 1, NULL));
349 }
350
351 return 0;
352 }
353
354 #define TEST_FUNCTION do_test ()
355 #include "../test-skeleton.c"
356