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 <pthread.h>
19 #include <shlib-compat.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <unistd.h>
23 
24 /* LinuxThreads pthread_cleanup_{push,pop} helpers.  */
25 extern void _pthread_cleanup_push (struct _pthread_cleanup_buffer *__buffer,
26                                    void (*__routine) (void *),
27                                    void *__arg);
28 compat_symbol_reference (libpthread, _pthread_cleanup_push,
29                          _pthread_cleanup_push, GLIBC_2_0);
30 extern void _pthread_cleanup_pop (struct _pthread_cleanup_buffer *__buffer,
31                                   int __execute);
32 compat_symbol_reference (libpthread, _pthread_cleanup_pop,
33                          _pthread_cleanup_pop, GLIBC_2_0);
34 
35 static int fds[2];
36 static pthread_barrier_t b2;
37 static int global;
38 
39 /* Defined in tst-cleanup4aux.c, never compiled with -fexceptions.  */
40 extern void fn5 (void);
41 extern void fn7 (void);
42 extern void fn9 (void);
43 
44 void
clh(void * arg)45 clh (void *arg)
46 {
47   int val = (long int) arg;
48 
49   printf ("clh (%d)\n", val);
50 
51   global *= val;
52   global += val;
53 }
54 
55 
56 static __attribute__((noinline)) void
fn_read(void)57 fn_read (void)
58 {
59   int r = pthread_barrier_wait (&b2);
60   if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD)
61     {
62       printf ("%s: barrier_wait failed\n", __FUNCTION__);
63       exit (1);
64     }
65 
66   char c;
67   read (fds[0], &c, 1);
68 }
69 
70 
71 __attribute__((noinline)) void
fn0(void)72 fn0 (void)
73 {
74   pthread_cleanup_push (clh, (void *) 1l);
75 
76   fn_read ();
77 
78   pthread_cleanup_pop (1);
79 }
80 
81 
82 __attribute__((noinline)) void
fn1(void)83 fn1 (void)
84 {
85   /* This is the old LinuxThreads pthread_cleanup_{push,pop}.  */
86   struct _pthread_cleanup_buffer b;
87   _pthread_cleanup_push (&b, clh, (void *) 2l);
88 
89   fn0 ();
90 
91   _pthread_cleanup_pop (&b, 1);
92 }
93 
94 
95 static __attribute__((noinline)) void
fn2(void)96 fn2 (void)
97 {
98   pthread_cleanup_push (clh, (void *) 3l);
99 
100   fn1 ();
101 
102   pthread_cleanup_pop (1);
103 }
104 
105 
106 static void *
tf(void * a)107 tf (void *a)
108 {
109   switch ((long) a)
110     {
111     case 0:
112       fn2 ();
113       break;
114     case 1:
115       fn5 ();
116       break;
117     case 2:
118       fn7 ();
119       break;
120     case 3:
121       fn9 ();
122       break;
123     }
124 
125   return NULL;
126 }
127 
128 
129 int
do_test(void)130 do_test (void)
131 {
132   int result = 0;
133 
134   if (pipe (fds) != 0)
135     {
136       puts ("pipe failed");
137       exit (1);
138     }
139 
140   if (pthread_barrier_init (&b2, NULL, 2) != 0)
141     {
142       puts ("b2 init failed");
143       exit (1);
144     }
145 
146   const int expect[] =
147     {
148       15,	/* 1 2 3 */
149       276,	/* 1 4 5 6 */
150       120,	/* 1 7 8 */
151       460	/* 1 2 9 10 */
152     };
153 
154   long i;
155   for (i = 0; i < 4; ++i)
156     {
157       global = 0;
158 
159       printf ("test %ld\n", i);
160 
161       pthread_t th;
162       if (pthread_create (&th, NULL, tf, (void *) i) != 0)
163 	{
164 	  puts ("create failed");
165 	  exit (1);
166 	}
167 
168       int e = pthread_barrier_wait (&b2);
169       if (e != 0 && e != PTHREAD_BARRIER_SERIAL_THREAD)
170 	{
171 	  printf ("%s: barrier_wait failed\n", __FUNCTION__);
172 	  exit (1);
173 	}
174 
175       pthread_cancel (th);
176 
177       void *r;
178       if ((e = pthread_join (th, &r)) != 0)
179 	{
180 	  printf ("join failed: %d\n", e);
181 	  _exit (1);
182 	}
183 
184       if (r != PTHREAD_CANCELED)
185 	{
186 	  puts ("thread not canceled");
187 	  exit (1);
188 	}
189 
190       if (global != expect[i])
191 	{
192 	  printf ("global = %d, expected %d\n", global, expect[i]);
193 	  result = 1;
194 	}
195     }
196 
197   return result;
198 }
199 
200 #define TEST_FUNCTION do_test ()
201 #include "../test-skeleton.c"
202