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