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/>. */
19 /* Which tests to perform.
20 Uncomment some of these, to verify that all tests crash if no locking
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
44 #include "virtualbox.h"
48 # define dbgprintf printf
50 # define dbgprintf if (0) printf
54 # define yield() thrd_yield ()
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
;
71 cnd_wait_routine (void *arg
)
73 ASSERT (mtx_lock (&lockcond
) == thrd_success
);
76 /* The main thread already slept, and nevertheless this thread comes
84 ASSERT (cnd_wait (&condtest
, &lockcond
) == thrd_success
);
88 ASSERT (mtx_unlock (&lockcond
) == thrd_success
);
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;
117 ret
= thrd_sleep (&remaining
, &remaining
);
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
);
126 ASSERT (cnd_signal (&condtest
) == thrd_success
);
127 ASSERT (mtx_unlock (&lockcond
) == thrd_success
);
129 ASSERT (thrd_join (thread
, NULL
) == thrd_success
);
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. */
148 get_ts (struct timespec
*ts
)
152 gettimeofday (&now
, NULL
);
154 ts
->tv_sec
= now
.tv_sec
+ 1;
155 ts
->tv_nsec
= now
.tv_usec
* 1000;
159 cnd_timedwait_routine (void *arg
)
164 ASSERT (mtx_lock (&lockcond
) == thrd_success
);
167 /* The main thread already slept, and nevertheless this thread comes
176 ret
= cnd_timedwait (&condtest
, &lockcond
, &ts
);
177 if (ret
== thrd_timedout
)
182 ASSERT (mtx_unlock (&lockcond
) == thrd_success
);
188 test_cnd_timedwait (void)
194 cond_value
= cond_timed_out
= 0;
196 /* Create a separate thread. */
197 ASSERT (thrd_create (&thread
, cnd_timedwait_routine
, &skipped
)
200 /* Sleep for 2 seconds. */
202 struct timespec remaining
;
204 remaining
.tv_sec
= 2;
205 remaining
.tv_nsec
= 0;
210 ret
= thrd_sleep (&remaining
, &remaining
);
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
);
219 ASSERT (cnd_signal (&condtest
) == thrd_success
);
220 ASSERT (mtx_unlock (&lockcond
) == thrd_success
);
222 ASSERT (thrd_join (thread
, NULL
) == thrd_success
);
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",
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
);
252 ASSERT (cnd_init (&condtest
) == thrd_success
);
253 ASSERT (mtx_init (&lockcond
, mtx_plain
) == thrd_success
);
256 printf ("Starting test_cnd_wait ..."); fflush (stdout
);
258 int skipped
= test_cnd_wait ();
259 printf (skipped
? " SKIP\n" : " OK\n"); fflush (stdout
);
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
);
270 return test_exit_status
;