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 <errno.h>
19 #include <pthread.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <unistd.h>
24 #include <sys/mman.h>
25 #include <sys/wait.h>
26 
27 
28 static pthread_barrier_t b2;
29 static int fd;
30 static int called;
31 
32 
33 static void
cl(void * arg)34 cl (void *arg)
35 {
36   called = 1;
37 }
38 
39 
40 static void *
tf(void * arg)41 tf (void *arg)
42 {
43   int r = pthread_barrier_wait (&b2);
44   if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD)
45     {
46       puts ("child thread: barrier_wait failed");
47       exit (1);
48     }
49 
50   pthread_cleanup_push (cl, NULL);
51 
52   /* This call should never return.  */
53   (void) lockf (fd, F_LOCK, 0);
54 
55   pthread_cleanup_pop (0);
56 
57   return NULL;
58 }
59 
60 
61 static int
do_test(void)62 do_test (void)
63 {
64   char fname[] = "/tmp/cancel16XXXXXX";
65   fd = mkstemp (fname);
66   if (fd == -1)
67     {
68       puts ("mkstemp failed");
69       return 1;
70     }
71   unlink (fname);
72 
73   char mem[sizeof (pthread_barrier_t)];
74   memset (mem, '\0', sizeof (mem));
75   if (TEMP_FAILURE_RETRY (pwrite (fd, mem, sizeof (mem), 0)) != sizeof (mem))
76     {
77       puts ("pwrite failed");
78       return 1;
79     }
80 
81   void *p = mmap (NULL, sizeof (mem), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
82   if (p == MAP_FAILED)
83     {
84       puts ("mmap failed");
85       return 1;
86     }
87   pthread_barrier_t *b = (pthread_barrier_t *) p;
88 
89   pthread_barrierattr_t ba;
90   if (pthread_barrierattr_init (&ba) != 0)
91     {
92       puts ("barrierattr_init failed");
93       return 1;
94     }
95   if (pthread_barrierattr_setpshared (&ba, 1) != 0)
96     {
97       puts ("barrierattr_setshared failed");
98       return 1;
99     }
100 
101   if (pthread_barrier_init (b, &ba, 2) != 0)
102     {
103       puts ("1st barrier_init failed");
104       return 1;
105     }
106   if (pthread_barrierattr_destroy (&ba) != 0)
107     {
108       puts ("barrier_destroy failed");
109       return 1;
110     }
111 
112   pid_t pid = fork ();
113   if (pid == 0)
114     {
115       /* Child.  Lock the file and wait.  */
116       if (lockf (fd, F_LOCK, 0) != 0)
117 	{
118 	  puts ("child process: lockf failed");
119 	  _exit (1);
120 	}
121 
122       int r = pthread_barrier_wait (b);
123       if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD)
124 	{
125 	  puts ("child process: 1st barrier_wait failed");
126 	  _exit (1);
127 	}
128 
129       /* Make sure the process dies.  */
130       alarm (5);
131 
132       r = pthread_barrier_wait (b);
133       if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD)
134 	{
135 	  puts ("child process: 2nd barrier_wait failed");
136 	  _exit (1);
137 	}
138 
139       _exit (0);
140     }
141   if (pid == -1)
142     {
143       puts ("fork failed");
144       return 1;
145     }
146 
147   int r = pthread_barrier_wait (b);
148   if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD)
149     {
150       puts ("main: 1st barrier_wait failed");
151       _exit (1);
152     }
153 
154   if (pthread_barrier_init (&b2, NULL, 2) != 0)
155     {
156       puts ("2nd barrier_init failed");
157       return 1;
158     }
159 
160   pthread_t th;
161   if (pthread_create (&th, NULL, tf, NULL) != 0)
162     {
163       puts ("create failed");
164       return 1;
165     }
166 
167   r = pthread_barrier_wait (&b2);
168   if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD)
169     {
170       puts ("main: 2nd barrier_wait failed");
171       return 1;
172     }
173 
174   /* Delay.  */
175   sleep (1);
176 
177   if (pthread_cancel (th) != 0)
178     {
179       puts ("cancel failed");
180       return 1;
181     }
182 
183   void *result;
184   if (pthread_join (th, &result) != 0)
185     {
186       puts ("join failed");
187       return 1;
188     }
189   if (result != PTHREAD_CANCELED)
190     {
191       puts ("thread not canceled");
192       return 1;
193     }
194   if (called == 0)
195     {
196       puts ("cleanup handler not called");
197       return 1;
198     }
199 
200   r = pthread_barrier_wait (b);
201   if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD)
202     {
203       puts ("main: 3rd barrier_wait failed");
204       return 1;
205     }
206 
207   int status;
208   if (TEMP_FAILURE_RETRY (waitpid (pid, &status, 0)) != pid)
209     {
210       puts ("waitpid failed");
211       return 1;
212     }
213   if (WEXITSTATUS (status) != 0)
214     {
215       printf ("child process exits with %d\n", WEXITSTATUS (status));
216       return 1;
217     }
218 
219   if (lockf (fd, F_LOCK, 0) != 0)
220     {
221       puts ("main: lockf failed");
222       return 1;
223     }
224 
225   return 0;
226 }
227 
228 #define TEST_FUNCTION do_test ()
229 #include "../test-skeleton.c"
230