gettext: Sync with gettext 0.23.
[gnulib.git] / tests / test-cnd.c
blobbcd0e7786c52d0a0872f587432f8ff979b586993
1 /* Test of condition variables in multithreaded situations.
2 Copyright (C) 2008-2024 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program 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
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <https://www.gnu.org/licenses/>. */
17 #include <config.h>
19 /* Which tests to perform.
20 Uncomment some of these, to verify that all tests crash if no locking
21 is enabled. */
22 #define DO_TEST_COND 1
23 #define DO_TEST_TIMEDCOND 1
26 /* Whether to help the scheduler through explicit thrd_yield().
27 Uncomment this to see if the operating system has a fair scheduler. */
28 #define EXPLICIT_YIELD 1
30 /* Whether to print debugging messages. */
31 #define ENABLE_DEBUGGING 0
33 #include <threads.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <sys/time.h>
39 #if HAVE_DECL_ALARM
40 # include <signal.h>
41 # include <unistd.h>
42 #endif
44 #include "virtualbox.h"
45 #include "macros.h"
47 #if ENABLE_DEBUGGING
48 # define dbgprintf printf
49 #else
50 # define dbgprintf if (0) printf
51 #endif
53 #if EXPLICIT_YIELD
54 # define yield() thrd_yield ()
55 #else
56 # define yield()
57 #endif
61 * Condition check
64 /* Marked volatile so that different threads see the same value. This is
65 good enough in practice, although in theory stdatomic.h should be used. */
66 static int volatile cond_value;
67 static cnd_t condtest;
68 static mtx_t lockcond;
70 static int
71 cnd_wait_routine (void *arg)
73 ASSERT (mtx_lock (&lockcond) == thrd_success);
74 if (cond_value)
76 /* The main thread already slept, and nevertheless this thread comes
77 too late. */
78 *(int *)arg = 1;
80 else
84 ASSERT (cnd_wait (&condtest, &lockcond) == thrd_success);
86 while (!cond_value);
88 ASSERT (mtx_unlock (&lockcond) == thrd_success);
90 cond_value = 2;
92 return 0;
95 static int
96 test_cnd_wait ()
98 int skipped = 0;
99 thrd_t thread;
100 int ret;
102 cond_value = 0;
104 /* Create a separate thread. */
105 ASSERT (thrd_create (&thread, cnd_wait_routine, &skipped) == thrd_success);
107 /* Sleep for 2 seconds. */
109 struct timespec remaining;
111 remaining.tv_sec = 2;
112 remaining.tv_nsec = 0;
116 yield ();
117 ret = thrd_sleep (&remaining, &remaining);
118 ASSERT (ret >= -1);
120 while (ret == -1 && (remaining.tv_sec != 0 || remaining.tv_nsec != 0));
123 /* Tell one of the waiting threads (if any) to continue. */
124 ASSERT (mtx_lock (&lockcond) == thrd_success);
125 cond_value = 1;
126 ASSERT (cnd_signal (&condtest) == thrd_success);
127 ASSERT (mtx_unlock (&lockcond) == thrd_success);
129 ASSERT (thrd_join (thread, NULL) == thrd_success);
131 if (cond_value != 2)
132 abort ();
134 return skipped;
139 * Timed Condition check
142 /* Marked volatile so that different threads see the same value. This is
143 good enough in practice, although in theory stdatomic.h should be used. */
144 static int volatile cond_timed_out;
146 /* Stores in *TS the current time plus 1 second. */
147 static void
148 get_ts (struct timespec *ts)
150 struct timeval now;
152 gettimeofday (&now, NULL);
154 ts->tv_sec = now.tv_sec + 1;
155 ts->tv_nsec = now.tv_usec * 1000;
158 static int
159 cnd_timedwait_routine (void *arg)
161 int ret;
162 struct timespec ts;
164 ASSERT (mtx_lock (&lockcond) == thrd_success);
165 if (cond_value)
167 /* The main thread already slept, and nevertheless this thread comes
168 too late. */
169 *(int *)arg = 1;
171 else
175 get_ts (&ts);
176 ret = cnd_timedwait (&condtest, &lockcond, &ts);
177 if (ret == thrd_timedout)
178 cond_timed_out = 1;
180 while (!cond_value);
182 ASSERT (mtx_unlock (&lockcond) == thrd_success);
184 return 0;
187 static int
188 test_cnd_timedwait (void)
190 int skipped = 0;
191 thrd_t thread;
192 int ret;
194 cond_value = cond_timed_out = 0;
196 /* Create a separate thread. */
197 ASSERT (thrd_create (&thread, cnd_timedwait_routine, &skipped)
198 == thrd_success);
200 /* Sleep for 2 seconds. */
202 struct timespec remaining;
204 remaining.tv_sec = 2;
205 remaining.tv_nsec = 0;
209 yield ();
210 ret = thrd_sleep (&remaining, &remaining);
211 ASSERT (ret >= -1);
213 while (ret == -1 && (remaining.tv_sec != 0 || remaining.tv_nsec != 0));
216 /* Tell one of the waiting threads (if any) to continue. */
217 ASSERT (mtx_lock (&lockcond) == thrd_success);
218 cond_value = 1;
219 ASSERT (cnd_signal (&condtest) == thrd_success);
220 ASSERT (mtx_unlock (&lockcond) == thrd_success);
222 ASSERT (thrd_join (thread, NULL) == thrd_success);
224 if (!cond_timed_out)
225 abort ();
227 return skipped;
232 main ()
234 /* This test occasionally fails on Linux (glibc or musl libc), in a
235 VirtualBox VM with paravirtualization = Default or KVM, with ≥ 2 CPUs.
236 Skip the test in this situation. */
237 if (is_running_under_virtualbox_kvm () && num_cpus () > 1)
239 fputs ("Skipping test: avoiding VirtualBox bug with KVM paravirtualization\n",
240 stderr);
241 return 77;
244 #if HAVE_DECL_ALARM
245 /* Declare failure if test takes too long, by using default abort
246 caused by SIGALRM. */
247 int alarm_value = 600;
248 signal (SIGALRM, SIG_DFL);
249 alarm (alarm_value);
250 #endif
252 ASSERT (cnd_init (&condtest) == thrd_success);
253 ASSERT (mtx_init (&lockcond, mtx_plain) == thrd_success);
255 #if DO_TEST_COND
256 printf ("Starting test_cnd_wait ..."); fflush (stdout);
258 int skipped = test_cnd_wait ();
259 printf (skipped ? " SKIP\n" : " OK\n"); fflush (stdout);
261 #endif
262 #if DO_TEST_TIMEDCOND
263 printf ("Starting test_cnd_timedwait ..."); fflush (stdout);
265 int skipped = test_cnd_timedwait ();
266 printf (skipped ? " SKIP\n" : " OK\n"); fflush (stdout);
268 #endif
270 return test_exit_status;