1/* Verify that condition variables synchronized by PI mutexes don't hang on
2 on cancellation.
3 Copyright (C) 2012-2024 Free Software Foundation, Inc.
4 This file is part of the GNU C Library.
5
6 The GNU C Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
10
11 The GNU C Library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public
17 License along with the GNU C Library; if not, see
18 <https://www.gnu.org/licenses/>. */
19
20#include <pthread.h>
21#include <stdio.h>
22#include <stdlib.h>
23#include <stdint.h>
24#include <string.h>
25#include <errno.h>
26#include <sys/types.h>
27#include <sys/syscall.h>
28#include <unistd.h>
29#include <sys/time.h>
30#include <time.h>
31
32#define NUM 5
33#define ITERS 10000
34#define COUNT 100
35
36typedef void *(*thr_func) (void *);
37
38pthread_mutex_t mutex;
39pthread_cond_t cond;
40
41void cleanup (void *u)
42{
43 /* pthread_cond_wait should always return with the mutex locked. The
44 pthread_mutex_unlock implementation does not actually check whether we
45 own the mutex for several mutex kinds, so check this explicitly. */
46 int ret = pthread_mutex_trylock (mutex: &mutex);
47 if (ret != EDEADLK && ret != EBUSY)
48 {
49 printf (format: "mutex not locked in cleanup %d\n", ret);
50 abort ();
51 }
52 if (pthread_mutex_unlock (mutex: &mutex))
53 abort ();
54}
55
56void *
57signaller (void *u)
58{
59 int i, ret = 0;
60 void *tret = NULL;
61
62 for (i = 0; i < ITERS; i++)
63 {
64 if ((ret = pthread_mutex_lock (mutex: &mutex)) != 0)
65 {
66 tret = (void *)1;
67 printf (format: "signaller:mutex_lock failed: %s\n", strerror (errnum: ret));
68 goto out;
69 }
70 if ((ret = pthread_cond_signal (cond: &cond)) != 0)
71 {
72 tret = (void *)1;
73 printf (format: "signaller:signal failed: %s\n", strerror (errnum: ret));
74 goto unlock_out;
75 }
76 if ((ret = pthread_mutex_unlock (mutex: &mutex)) != 0)
77 {
78 tret = (void *)1;
79 printf (format: "signaller:mutex_unlock failed: %s\n", strerror (errnum: ret));
80 goto out;
81 }
82 pthread_testcancel ();
83 }
84
85out:
86 return tret;
87
88unlock_out:
89 if ((ret = pthread_mutex_unlock (mutex: &mutex)) != 0)
90 printf (format: "signaller:mutex_unlock[2] failed: %s\n", strerror (errnum: ret));
91 goto out;
92}
93
94void *
95waiter (void *u)
96{
97 int i, ret = 0;
98 void *tret = NULL;
99 int seq = (uintptr_t) u;
100
101 for (i = 0; i < ITERS / NUM; i++)
102 {
103 if ((ret = pthread_mutex_lock (mutex: &mutex)) != 0)
104 {
105 tret = (void *) (uintptr_t) 1;
106 printf (format: "waiter[%u]:mutex_lock failed: %s\n", seq, strerror (errnum: ret));
107 goto out;
108 }
109 pthread_cleanup_push (cleanup, NULL);
110
111 if ((ret = pthread_cond_wait (cond: &cond, mutex: &mutex)) != 0)
112 {
113 tret = (void *) (uintptr_t) 1;
114 printf (format: "waiter[%u]:wait failed: %s\n", seq, strerror (errnum: ret));
115 goto unlock_out;
116 }
117
118 if ((ret = pthread_mutex_unlock (mutex: &mutex)) != 0)
119 {
120 tret = (void *) (uintptr_t) 1;
121 printf (format: "waiter[%u]:mutex_unlock failed: %s\n", seq, strerror (errnum: ret));
122 goto out;
123 }
124 pthread_cleanup_pop (0);
125 }
126
127out:
128 puts (s: "waiter tests done");
129 return tret;
130
131unlock_out:
132 if ((ret = pthread_mutex_unlock (mutex: &mutex)) != 0)
133 printf (format: "waiter:mutex_unlock[2] failed: %s\n", strerror (errnum: ret));
134 goto out;
135}
136
137void *
138timed_waiter (void *u)
139{
140 int i, ret;
141 void *tret = NULL;
142 int seq = (uintptr_t) u;
143
144 for (i = 0; i < ITERS / NUM; i++)
145 {
146 struct timespec ts;
147
148 if ((ret = clock_gettime(CLOCK_REALTIME, tp: &ts)) != 0)
149 {
150 tret = (void *) (uintptr_t) 1;
151 printf (format: "%u:clock_gettime failed: %s\n", seq, strerror (errno));
152 goto out;
153 }
154 ts.tv_sec += 20;
155
156 if ((ret = pthread_mutex_lock (mutex: &mutex)) != 0)
157 {
158 tret = (void *) (uintptr_t) 1;
159 printf (format: "waiter[%u]:mutex_lock failed: %s\n", seq, strerror (errnum: ret));
160 goto out;
161 }
162 pthread_cleanup_push (cleanup, NULL);
163
164 /* We should not time out either. */
165 if ((ret = pthread_cond_timedwait (cond: &cond, mutex: &mutex, abstime: &ts)) != 0)
166 {
167 tret = (void *) (uintptr_t) 1;
168 printf (format: "waiter[%u]:timedwait failed: %s\n", seq, strerror (errnum: ret));
169 goto unlock_out;
170 }
171 if ((ret = pthread_mutex_unlock (mutex: &mutex)) != 0)
172 {
173 tret = (void *) (uintptr_t) 1;
174 printf (format: "waiter[%u]:mutex_unlock failed: %s\n", seq, strerror (errnum: ret));
175 goto out;
176 }
177 pthread_cleanup_pop (0);
178 }
179
180out:
181 puts (s: "timed_waiter tests done");
182 return tret;
183
184unlock_out:
185 if ((ret = pthread_mutex_unlock (mutex: &mutex)) != 0)
186 printf (format: "waiter[%u]:mutex_unlock[2] failed: %s\n", seq, strerror (errnum: ret));
187 goto out;
188}
189
190int
191do_test_wait (thr_func f)
192{
193 pthread_t w[NUM];
194 pthread_t s;
195 pthread_mutexattr_t attr;
196 int i, j, ret = 0;
197 void *thr_ret;
198
199 for (i = 0; i < COUNT; i++)
200 {
201 if ((ret = pthread_mutexattr_init (attr: &attr)) != 0)
202 {
203 printf (format: "mutexattr_init failed: %s\n", strerror (errnum: ret));
204 goto out;
205 }
206
207 if ((ret = pthread_mutexattr_setprotocol (attr: &attr,
208 protocol: PTHREAD_PRIO_INHERIT)) != 0)
209 {
210 printf (format: "mutexattr_setprotocol failed: %s\n", strerror (errnum: ret));
211 goto out;
212 }
213
214 if ((ret = pthread_cond_init (cond: &cond, NULL)) != 0)
215 {
216 printf (format: "cond_init failed: %s\n", strerror (errnum: ret));
217 goto out;
218 }
219
220 if ((ret = pthread_mutex_init (mutex: &mutex, mutexattr: &attr)) != 0)
221 {
222 printf (format: "mutex_init failed: %s\n", strerror (errnum: ret));
223 goto out;
224 }
225
226 for (j = 0; j < NUM; j++)
227 if ((ret = pthread_create (newthread: &w[j], NULL,
228 start_routine: f, arg: (void *) (uintptr_t) j)) != 0)
229 {
230 printf (format: "waiter[%d]: create failed: %s\n", j, strerror (errnum: ret));
231 goto out;
232 }
233
234 if ((ret = pthread_create (newthread: &s, NULL, start_routine: signaller, NULL)) != 0)
235 {
236 printf (format: "signaller: create failed: %s\n", strerror (errnum: ret));
237 goto out;
238 }
239
240 for (j = 0; j < NUM; j++)
241 {
242 pthread_cancel (th: w[j]);
243
244 if ((ret = pthread_join (th: w[j], thread_return: &thr_ret)) != 0)
245 {
246 printf (format: "waiter[%d]: join failed: %s\n", j, strerror (errnum: ret));
247 goto out;
248 }
249
250 if (thr_ret != NULL && thr_ret != PTHREAD_CANCELED)
251 {
252 ret = 1;
253 goto out;
254 }
255 }
256
257 /* The signalling thread could have ended before it was cancelled. */
258 pthread_cancel (th: s);
259
260 if ((ret = pthread_join (th: s, thread_return: &thr_ret)) != 0)
261 {
262 printf (format: "signaller: join failed: %s\n", strerror (errnum: ret));
263 goto out;
264 }
265
266 if (thr_ret != NULL && thr_ret != PTHREAD_CANCELED)
267 {
268 ret = 1;
269 goto out;
270 }
271 }
272
273out:
274 return ret;
275}
276
277int
278do_test (int argc, char **argv)
279{
280 int ret = do_test_wait (f: waiter);
281
282 if (ret)
283 return ret;
284
285 return do_test_wait (f: timed_waiter);
286}
287
288#include "../test-skeleton.c"
289

source code of glibc/sysdeps/pthread/tst-cond25.c