1 | /* Copyright (C) 2003-2024 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 |
31 | wait_code (void) |
32 | { |
33 | struct timespec ts = { .tv_sec = 0, .tv_nsec = 200000000 }; |
34 | while (nanosleep (requested_time: &ts, remaining: &ts) < 0) |
35 | ; |
36 | } |
37 | |
38 | |
39 | #ifdef WAIT_IN_CHILD |
40 | static pthread_barrier_t b; |
41 | #endif |
42 | |
43 | static int |
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 (s: 1000, ns: 0)); |
53 | return pthread_clockjoin_np (th: thread, thread_return: retval, CLOCK_REALTIME, abstime: &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 * |
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 (thread: (pthread_t) arg, NULL); |
74 | |
75 | exit (42); |
76 | } |
77 | |
78 | |
79 | static void * |
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 (thread: (pthread_t) arg, NULL); |
89 | |
90 | exit (43); |
91 | } |
92 | |
93 | |
94 | static int |
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 (thread: pthread_self (), NULL); |
104 | if (err == 0) |
105 | { |
106 | puts (s: "1st circular join succeeded" ); |
107 | return 1; |
108 | } |
109 | if (err != EDEADLK) |
110 | { |
111 | printf (format: "1st circular join %d, not EDEADLK\n" , err); |
112 | return 1; |
113 | } |
114 | |
115 | th = xpthread_create (NULL, thread_func: tf1, closure: (void *) pthread_self ()); |
116 | |
117 | #ifndef WAIT_IN_CHILD |
118 | wait_code (); |
119 | #endif |
120 | |
121 | xpthread_cancel (thr: th); |
122 | |
123 | #ifdef WAIT_IN_CHILD |
124 | xpthread_barrier_wait (&b); |
125 | #endif |
126 | |
127 | void *r; |
128 | err = thread_join (thread: th, retval: &r); |
129 | if (err != 0) |
130 | { |
131 | printf (format: "cannot join 1st thread: %d\n" , err); |
132 | return 1; |
133 | } |
134 | if (r != PTHREAD_CANCELED) |
135 | { |
136 | puts (s: "1st thread not canceled" ); |
137 | return 1; |
138 | } |
139 | |
140 | err = thread_join (thread: pthread_self (), NULL); |
141 | if (err == 0) |
142 | { |
143 | puts (s: "2nd circular join succeeded" ); |
144 | return 1; |
145 | } |
146 | if (err != EDEADLK) |
147 | { |
148 | printf (format: "2nd circular join %d, not EDEADLK\n" , err); |
149 | return 1; |
150 | } |
151 | |
152 | th = xpthread_create (NULL, thread_func: tf2, closure: (void *) pthread_self ()); |
153 | |
154 | #ifndef WAIT_IN_CHILD |
155 | wait_code (); |
156 | #endif |
157 | |
158 | xpthread_cancel (thr: th); |
159 | |
160 | #ifdef WAIT_IN_CHILD |
161 | xpthread_barrier_wait (&b); |
162 | #endif |
163 | |
164 | if (thread_join (thread: th, retval: &r) != 0) |
165 | { |
166 | puts (s: "cannot join 2nd thread" ); |
167 | return 1; |
168 | } |
169 | if (r != PTHREAD_CANCELED) |
170 | { |
171 | puts (s: "2nd thread not canceled" ); |
172 | return 1; |
173 | } |
174 | |
175 | err = thread_join (thread: pthread_self (), NULL); |
176 | if (err == 0) |
177 | { |
178 | puts (s: "3rd circular join succeeded" ); |
179 | return 1; |
180 | } |
181 | if (err != EDEADLK) |
182 | { |
183 | printf (format: "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 | |