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 <time.h>
23 #include <unistd.h>
24 
25 #include <support/check.h>
26 #include <support/timespec.h>
27 #include <support/xthread.h>
28 #include <support/xtime.h>
29 
30 static void
wait_code(void)31 wait_code (void)
32 {
33   struct timespec ts = { .tv_sec = 0, .tv_nsec = 200000000 };
34   while (nanosleep (&ts, &ts) < 0)
35     ;
36 }
37 
38 
39 #ifdef WAIT_IN_CHILD
40 static pthread_barrier_t b;
41 #endif
42 
43 static int
thread_join(pthread_t thread,void ** retval)44 thread_join (pthread_t thread, void **retval)
45 {
46 #if defined USE_PTHREAD_TIMEDJOIN_NP
47   const struct timespec ts = timespec_add (xclock_now (CLOCK_REALTIME),
48                                            make_timespec (1000, 0));
49   return pthread_timedjoin_np (thread, retval, &ts);
50 #elif defined USE_PTHREAD_CLOCKJOIN_NP_REALTIME
51   const struct timespec ts = timespec_add (xclock_now (CLOCK_REALTIME),
52                                            make_timespec (1000, 0));
53   return pthread_clockjoin_np (thread, retval, CLOCK_REALTIME, &ts);
54 #elif defined USE_PTHREAD_CLOCKJOIN_NP_MONOTONIC
55   const struct timespec ts = timespec_add (xclock_now (CLOCK_MONOTONIC),
56                                            make_timespec (1000, 0));
57   return pthread_clockjoin_np (thread, retval, CLOCK_MONOTONIC, &ts);
58 #else
59   return pthread_join (thread, retval);
60 #endif
61 }
62 
63 
64 static void *
tf1(void * arg)65 tf1 (void *arg)
66 {
67 #ifdef WAIT_IN_CHILD
68   xpthread_barrier_wait (&b);
69 
70   wait_code ();
71 #endif
72 
73   thread_join ((pthread_t) arg, NULL);
74 
75   exit (42);
76 }
77 
78 
79 static void *
tf2(void * arg)80 tf2 (void *arg)
81 {
82 #ifdef WAIT_IN_CHILD
83   xpthread_barrier_wait (&b);
84 
85   wait_code ();
86 #endif
87 
88   thread_join ((pthread_t) arg, NULL);
89 
90   exit (43);
91 }
92 
93 
94 static int
do_test(void)95 do_test (void)
96 {
97 #ifdef WAIT_IN_CHILD
98   xpthread_barrier_init (&b, NULL, 2);
99 #endif
100 
101   pthread_t th;
102 
103   int err = thread_join (pthread_self (), NULL);
104   if (err == 0)
105     {
106       puts ("1st circular join succeeded");
107       return 1;
108     }
109   if (err != EDEADLK)
110     {
111       printf ("1st circular join %d, not EDEADLK\n", err);
112       return 1;
113     }
114 
115   th = xpthread_create (NULL, tf1, (void *) pthread_self ());
116 
117 #ifndef WAIT_IN_CHILD
118   wait_code ();
119 #endif
120 
121   xpthread_cancel (th);
122 
123 #ifdef WAIT_IN_CHILD
124   xpthread_barrier_wait (&b);
125 #endif
126 
127   void *r;
128   err = thread_join (th, &r);
129   if (err != 0)
130     {
131       printf ("cannot join 1st thread: %d\n", err);
132       return 1;
133     }
134   if (r != PTHREAD_CANCELED)
135     {
136       puts ("1st thread not canceled");
137       return 1;
138     }
139 
140   err = thread_join (pthread_self (), NULL);
141   if (err == 0)
142     {
143       puts ("2nd circular join succeeded");
144       return 1;
145     }
146   if (err != EDEADLK)
147     {
148       printf ("2nd circular join %d, not EDEADLK\n", err);
149       return 1;
150     }
151 
152   th = xpthread_create (NULL, tf2, (void *) pthread_self ());
153 
154 #ifndef WAIT_IN_CHILD
155   wait_code ();
156 #endif
157 
158   xpthread_cancel (th);
159 
160 #ifdef WAIT_IN_CHILD
161   xpthread_barrier_wait (&b);
162 #endif
163 
164   if (thread_join (th, &r) != 0)
165     {
166       puts ("cannot join 2nd thread");
167       return 1;
168     }
169   if (r != PTHREAD_CANCELED)
170     {
171       puts ("2nd thread not canceled");
172       return 1;
173     }
174 
175   err = thread_join (pthread_self (), NULL);
176   if (err == 0)
177     {
178       puts ("3rd circular join succeeded");
179       return 1;
180     }
181   if (err != EDEADLK)
182     {
183       printf ("3rd circular join %d, not EDEADLK\n", err);
184       return 1;
185     }
186 
187   return 0;
188 }
189 
190 #include <support/test-driver.c>
191