I wrote:
> Even though the futex was supposed to time out at 1719588805.808799
> it terminated only at 1719588806.863658
> which is more than 1 second too late.
>
> It seems that this is the same bug as
> <https://sourceware.org/bugzilla/show_bug.cgi?id=25847>.
Wrong guess. It is *not* this glibc bug.
First, I polished this test (tests/test-pthread-cond.c) and its two
siblings, fixing bugs in the test:
- Use of an 'int' variable to communicate between two threads,
where at least one of the accesses was not in the scope of a lock.
I marked it 'volatile'. (Also tried '_Atomic int volatile', but
that did not make a difference.)
- Fix a race condition that could occur if one thread does not get
woken up for 2 seconds (likely would only occur under very high
load).
The test still sometimes failed on Debian 10.7.
On an Alpine Linux 3.20 VM, also with 8 threads, the test fails in the
same way.
So, if it was a glibc bug, it would have to be a musl bug as well. That
seemed unlikely, since musl libc generally has simpler code.
The suspicion thus moved to the hypervisor. I tried a couple of VMs under
QEMU (with '-smp 8' for 8 CPUs, and on x86_64 with '-enable-kvm'), and
there the test succeeds.
So, it looked to be specific to VirtualBox.
I turned to the VirtualBox settings, more precisely to the "Paravirtualization"
acceleration setting. Result:
Default, KVM -> test fails occasionally (about 1 in 3 times)
None, Legacy, Minimal, Hyper-V -> test succeeds (no failure in 20 runs)
With that, I'm documenting it as a bug specific to VirtualBox.
2024-06-28 Bruno Haible <[email protected]>
doc: Mention pthread_cond_timedwait bug caused by hypervisor.
* doc/posix-functions/pthread_cond_timedwait.texi: Mention VirtualBox
specific bug.
* doc/posix-functions/cnd_timedwait.texi: Likewise.
2024-06-28 Bruno Haible <[email protected]>
cond tests: Avoid theoretically possible failure.
* tests/test-cnd.c (cnd_wait_routine, cnd_timedwait_routine): Report to
the main thread if this thread comes too late.
(test_cnd_wait, test_cnd_timedwait): Return a 'skipped' value.
(main): Print SKIP instead of OK if the essence of the test was skipped.
* tests/test-cond.c (cond_routine, timedcond_routine): Report to the
main thread if this thread comes too late.
(test_cond, test_timedcond): Return a 'skipped' value.
(main): Print SKIP instead of OK if the essence of the test was skipped.
* tests/test-pthread-cond.c (pthread_cond_wait_routine,
pthread_cond_timedwait_routine): Report to the main thread if this
thread comes too late.
(test_pthread_cond_wait, test_pthread_cond_timedwait): Return a
'skipped' value.
(main): Print SKIP instead of OK if the essence of the test was skipped.
cond tests: Improve multithread-safety.
* tests/test-cnd.c (cond_value, cond_timed_out): Mark as volatile.
* tests/test-cond.c (cond_value, cond_timed_out): Likewise.
* tests/test-pthread-cond.c (cond_value, cond_timed_out): Likewise.
cond tests: Improve comments.
* tests/test-cnd.c: Improve comments.
(cond_value): Remove initializer.
(cond_timed_out): Renamed from cond_timeout.
* tests/test-cond.c: Improve comments.
(cond_value): Remove initializer.
(cond_timed_out): Renamed from cond_timeout.
* tests/test-pthread-cond.c: Improve comments.
(cond_value): Remove initializer.
(cond_timed_out): Renamed from cond_timeout.
>From d7b8b1ccc988290277945f52e1582dd669a97d7f Mon Sep 17 00:00:00 2001
From: Bruno Haible <[email protected]>
Date: Fri, 28 Jun 2024 21:40:16 +0200
Subject: [PATCH 1/4] cond tests: Improve comments.
* tests/test-cnd.c: Improve comments.
(cond_value): Remove initializer.
(cond_timed_out): Renamed from cond_timeout.
* tests/test-cond.c: Improve comments.
(cond_value): Remove initializer.
(cond_timed_out): Renamed from cond_timeout.
* tests/test-pthread-cond.c: Improve comments.
(cond_value): Remove initializer.
(cond_timed_out): Renamed from cond_timeout.
---
ChangeLog | 13 +++++++
tests/test-cnd.c | 71 +++++++++++++++++++++++----------------
tests/test-cond.c | 55 ++++++++++++++++++------------
tests/test-pthread-cond.c | 71 +++++++++++++++++++++++----------------
4 files changed, 131 insertions(+), 79 deletions(-)
diff --git a/ChangeLog b/ChangeLog
index a9453b0b45..a309c59a50 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,16 @@
+2024-06-28 Bruno Haible <[email protected]>
+
+ cond tests: Improve comments.
+ * tests/test-cnd.c: Improve comments.
+ (cond_value): Remove initializer.
+ (cond_timed_out): Renamed from cond_timeout.
+ * tests/test-cond.c: Improve comments.
+ (cond_value): Remove initializer.
+ (cond_timed_out): Renamed from cond_timeout.
+ * tests/test-pthread-cond.c: Improve comments.
+ (cond_value): Remove initializer.
+ (cond_timed_out): Renamed from cond_timeout.
+
2024-06-28 Bruno Haible <[email protected]>
time: Fix test failure on FreeBSD.
diff --git a/tests/test-cnd.c b/tests/test-cnd.c
index 61a86a859c..0aca5cdf07 100644
--- a/tests/test-cnd.c
+++ b/tests/test-cnd.c
@@ -59,7 +59,7 @@
/*
* Condition check
*/
-static int cond_value = 0;
+static int cond_value;
static cnd_t condtest;
static mtx_t lockcond;
@@ -81,25 +81,31 @@ cnd_wait_routine (void *arg)
static void
test_cnd_wait ()
{
- struct timespec remain;
thrd_t thread;
int ret;
- remain.tv_sec = 2;
- remain.tv_nsec = 0;
-
cond_value = 0;
+ /* Create a separate thread. */
ASSERT (thrd_create (&thread, cnd_wait_routine, NULL) == thrd_success);
- do
- {
- yield ();
- ret = thrd_sleep (&remain, &remain);
- ASSERT (ret >= -1);
- }
- while (ret == -1 && (remain.tv_sec != 0 || remain.tv_nsec != 0));
- /* signal condition */
+ /* Sleep for 2 seconds. */
+ {
+ struct timespec remaining;
+
+ remaining.tv_sec = 2;
+ remaining.tv_nsec = 0;
+
+ do
+ {
+ yield ();
+ ret = thrd_sleep (&remaining, &remaining);
+ ASSERT (ret >= -1);
+ }
+ while (ret == -1 && (remaining.tv_sec != 0 || remaining.tv_nsec != 0));
+ }
+
+ /* Tell one of the waiting threads (if any) to continue. */
ASSERT (mtx_lock (&lockcond) == thrd_success);
cond_value = 1;
ASSERT (cnd_signal (&condtest) == thrd_success);
@@ -115,8 +121,9 @@ test_cnd_wait ()
/*
* Timed Condition check
*/
-static int cond_timeout;
+static int cond_timed_out;
+/* Stores in *TS the current time plus 1 second. */
static void
get_ts (struct timespec *ts)
{
@@ -140,7 +147,7 @@ cnd_timedwait_routine (void *arg)
get_ts (&ts);
ret = cnd_timedwait (&condtest, &lockcond, &ts);
if (ret == thrd_timedout)
- cond_timeout = 1;
+ cond_timed_out = 1;
}
ASSERT (mtx_unlock (&lockcond) == thrd_success);
@@ -150,25 +157,31 @@ cnd_timedwait_routine (void *arg)
static void
test_cnd_timedwait (void)
{
- struct timespec remain;
thrd_t thread;
int ret;
- remain.tv_sec = 2;
- remain.tv_nsec = 0;
-
- cond_value = cond_timeout = 0;
+ cond_value = cond_timed_out = 0;
+ /* Create a separate thread. */
ASSERT (thrd_create (&thread, cnd_timedwait_routine, NULL) == thrd_success);
- do
- {
- yield ();
- ret = thrd_sleep (&remain, &remain);
- ASSERT (ret >= -1);
- }
- while (ret == -1 && (remain.tv_sec != 0 || remain.tv_nsec != 0));
- /* signal condition */
+ /* Sleep for 2 seconds. */
+ {
+ struct timespec remaining;
+
+ remaining.tv_sec = 2;
+ remaining.tv_nsec = 0;
+
+ do
+ {
+ yield ();
+ ret = thrd_sleep (&remaining, &remaining);
+ ASSERT (ret >= -1);
+ }
+ while (ret == -1 && (remaining.tv_sec != 0 || remaining.tv_nsec != 0));
+ }
+
+ /* Tell one of the waiting threads (if any) to continue. */
ASSERT (mtx_lock (&lockcond) == thrd_success);
cond_value = 1;
ASSERT (cnd_signal (&condtest) == thrd_success);
@@ -176,7 +189,7 @@ test_cnd_timedwait (void)
ASSERT (thrd_join (thread, NULL) == thrd_success);
- if (!cond_timeout)
+ if (!cond_timed_out)
abort ();
}
diff --git a/tests/test-cond.c b/tests/test-cond.c
index a9b1e03406..eb92580e44 100644
--- a/tests/test-cond.c
+++ b/tests/test-cond.c
@@ -59,7 +59,7 @@
/*
* Condition check
*/
-static int cond_value = 0;
+static int cond_value;
gl_cond_define_initialized(static, condtest)
gl_lock_define_initialized(static, lockcond)
@@ -81,20 +81,26 @@ cond_routine (void *arg)
static void
test_cond ()
{
- int remain = 2;
gl_thread_t thread;
cond_value = 0;
+ /* Create a separate thread. */
thread = gl_thread_create (cond_routine, NULL);
- do
- {
- yield ();
- remain = sleep (remain);
- }
- while (remain);
- /* signal condition */
+ /* Sleep for 2 seconds. */
+ {
+ int remaining = 2;
+
+ do
+ {
+ yield ();
+ remaining = sleep (remaining);
+ }
+ while (remaining);
+ }
+
+ /* Tell one of the waiting threads (if any) to continue. */
gl_lock_lock (lockcond);
cond_value = 1;
gl_cond_signal (condtest);
@@ -110,8 +116,9 @@ test_cond ()
/*
* Timed Condition check
*/
-static int cond_timeout;
+static int cond_timed_out;
+/* Stores in *TS the current time plus 1 second. */
static void
get_ts (struct timespec *ts)
{
@@ -135,7 +142,7 @@ timedcond_routine (void *arg)
get_ts (&ts);
ret = glthread_cond_timedwait (&condtest, &lockcond, &ts);
if (ret == ETIMEDOUT)
- cond_timeout = 1;
+ cond_timed_out = 1;
}
gl_lock_unlock (lockcond);
@@ -145,20 +152,26 @@ timedcond_routine (void *arg)
static void
test_timedcond (void)
{
- int remain = 2;
gl_thread_t thread;
- cond_value = cond_timeout = 0;
+ cond_value = cond_timed_out = 0;
+ /* Create a separate thread. */
thread = gl_thread_create (timedcond_routine, NULL);
- do
- {
- yield ();
- remain = sleep (remain);
- }
- while (remain);
- /* signal condition */
+ /* Sleep for 2 seconds. */
+ {
+ int remaining = 2;
+
+ do
+ {
+ yield ();
+ remaining = sleep (remaining);
+ }
+ while (remaining);
+ }
+
+ /* Tell one of the waiting threads (if any) to continue. */
gl_lock_lock (lockcond);
cond_value = 1;
gl_cond_signal (condtest);
@@ -166,7 +179,7 @@ test_timedcond (void)
gl_thread_join (thread, NULL);
- if (!cond_timeout)
+ if (!cond_timed_out)
abort ();
}
diff --git a/tests/test-pthread-cond.c b/tests/test-pthread-cond.c
index 145d4b4666..40cba9e548 100644
--- a/tests/test-pthread-cond.c
+++ b/tests/test-pthread-cond.c
@@ -64,7 +64,7 @@
/*
* Condition check
*/
-static int cond_value = 0;
+static int cond_value;
static pthread_cond_t condtest;
static pthread_mutex_t lockcond;
@@ -86,25 +86,31 @@ pthread_cond_wait_routine (void *arg)
static void
test_pthread_cond_wait ()
{
- struct timespec remain;
pthread_t thread;
int ret;
- remain.tv_sec = 2;
- remain.tv_nsec = 0;
-
cond_value = 0;
+ /* Create a separate thread. */
ASSERT (pthread_create (&thread, NULL, pthread_cond_wait_routine, NULL) == 0);
- do
- {
- yield ();
- ret = nanosleep (&remain, &remain);
- ASSERT (ret >= -1);
- }
- while (ret == -1 && (remain.tv_sec != 0 || remain.tv_nsec != 0));
- /* signal condition */
+ /* Sleep for 2 seconds. */
+ {
+ struct timespec remaining;
+
+ remaining.tv_sec = 2;
+ remaining.tv_nsec = 0;
+
+ do
+ {
+ yield ();
+ ret = nanosleep (&remaining, &remaining);
+ ASSERT (ret >= -1);
+ }
+ while (ret == -1 && (remaining.tv_sec != 0 || remaining.tv_nsec != 0));
+ }
+
+ /* Tell one of the waiting threads (if any) to continue. */
ASSERT (pthread_mutex_lock (&lockcond) == 0);
cond_value = 1;
ASSERT (pthread_cond_signal (&condtest) == 0);
@@ -120,8 +126,9 @@ test_pthread_cond_wait ()
/*
* Timed Condition check
*/
-static int cond_timeout;
+static int cond_timed_out;
+/* Stores in *TS the current time plus 1 second. */
static void
get_ts (struct timespec *ts)
{
@@ -145,7 +152,7 @@ pthread_cond_timedwait_routine (void *arg)
get_ts (&ts);
ret = pthread_cond_timedwait (&condtest, &lockcond, &ts);
if (ret == ETIMEDOUT)
- cond_timeout = 1;
+ cond_timed_out = 1;
}
ASSERT (pthread_mutex_unlock (&lockcond) == 0);
@@ -155,26 +162,32 @@ pthread_cond_timedwait_routine (void *arg)
static void
test_pthread_cond_timedwait (void)
{
- struct timespec remain;
pthread_t thread;
int ret;
- remain.tv_sec = 2;
- remain.tv_nsec = 0;
-
- cond_value = cond_timeout = 0;
+ cond_value = cond_timed_out = 0;
+ /* Create a separate thread. */
ASSERT (pthread_create (&thread, NULL, pthread_cond_timedwait_routine, NULL)
== 0);
- do
- {
- yield ();
- ret = nanosleep (&remain, &remain);
- ASSERT (ret >= -1);
- }
- while (ret == -1 && (remain.tv_sec != 0 || remain.tv_nsec != 0));
- /* signal condition */
+ /* Sleep for 2 seconds. */
+ {
+ struct timespec remaining;
+
+ remaining.tv_sec = 2;
+ remaining.tv_nsec = 0;
+
+ do
+ {
+ yield ();
+ ret = nanosleep (&remaining, &remaining);
+ ASSERT (ret >= -1);
+ }
+ while (ret == -1 && (remaining.tv_sec != 0 || remaining.tv_nsec != 0));
+ }
+
+ /* Tell one of the waiting threads (if any) to continue. */
ASSERT (pthread_mutex_lock (&lockcond) == 0);
cond_value = 1;
ASSERT (pthread_cond_signal (&condtest) == 0);
@@ -182,7 +195,7 @@ test_pthread_cond_timedwait (void)
ASSERT (pthread_join (thread, NULL) == 0);
- if (!cond_timeout)
+ if (!cond_timed_out)
abort ();
}
--
2.34.1
>From 60acac06739f191bf433afbfbacbe738e5df3be2 Mon Sep 17 00:00:00 2001
From: Bruno Haible <[email protected]>
Date: Fri, 28 Jun 2024 21:51:40 +0200
Subject: [PATCH 2/4] cond tests: Improve multithread-safety.
* tests/test-cnd.c (cond_value, cond_timed_out): Mark as volatile.
* tests/test-cond.c (cond_value, cond_timed_out): Likewise.
* tests/test-pthread-cond.c (cond_value, cond_timed_out): Likewise.
---
ChangeLog | 5 +++++
tests/test-cnd.c | 11 +++++++++--
tests/test-cond.c | 11 +++++++++--
tests/test-pthread-cond.c | 11 +++++++++--
4 files changed, 32 insertions(+), 6 deletions(-)
diff --git a/ChangeLog b/ChangeLog
index a309c59a50..75f9d19a43 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,10 @@
2024-06-28 Bruno Haible <[email protected]>
+ cond tests: Improve multithread-safety.
+ * tests/test-cnd.c (cond_value, cond_timed_out): Mark as volatile.
+ * tests/test-cond.c (cond_value, cond_timed_out): Likewise.
+ * tests/test-pthread-cond.c (cond_value, cond_timed_out): Likewise.
+
cond tests: Improve comments.
* tests/test-cnd.c: Improve comments.
(cond_value): Remove initializer.
diff --git a/tests/test-cnd.c b/tests/test-cnd.c
index 0aca5cdf07..730c879c49 100644
--- a/tests/test-cnd.c
+++ b/tests/test-cnd.c
@@ -59,7 +59,10 @@
/*
* Condition check
*/
-static int cond_value;
+
+/* Marked volatile so that different threads see the same value. This is
+ good enough in practice, although in theory stdatomic.h should be used. */
+static int volatile cond_value;
static cnd_t condtest;
static mtx_t lockcond;
@@ -121,7 +124,10 @@ test_cnd_wait ()
/*
* Timed Condition check
*/
-static int cond_timed_out;
+
+/* Marked volatile so that different threads see the same value. This is
+ good enough in practice, although in theory stdatomic.h should be used. */
+static int volatile cond_timed_out;
/* Stores in *TS the current time plus 1 second. */
static void
@@ -193,6 +199,7 @@ test_cnd_timedwait (void)
abort ();
}
+
int
main ()
{
diff --git a/tests/test-cond.c b/tests/test-cond.c
index eb92580e44..80d696a5d8 100644
--- a/tests/test-cond.c
+++ b/tests/test-cond.c
@@ -59,7 +59,10 @@
/*
* Condition check
*/
-static int cond_value;
+
+/* Marked volatile so that different threads see the same value. This is
+ good enough in practice, although in theory stdatomic.h should be used. */
+static int volatile cond_value;
gl_cond_define_initialized(static, condtest)
gl_lock_define_initialized(static, lockcond)
@@ -116,7 +119,10 @@ test_cond ()
/*
* Timed Condition check
*/
-static int cond_timed_out;
+
+/* Marked volatile so that different threads see the same value. This is
+ good enough in practice, although in theory stdatomic.h should be used. */
+static int volatile cond_timed_out;
/* Stores in *TS the current time plus 1 second. */
static void
@@ -183,6 +189,7 @@ test_timedcond (void)
abort ();
}
+
int
main ()
{
diff --git a/tests/test-pthread-cond.c b/tests/test-pthread-cond.c
index 40cba9e548..98c291c24c 100644
--- a/tests/test-pthread-cond.c
+++ b/tests/test-pthread-cond.c
@@ -64,7 +64,10 @@
/*
* Condition check
*/
-static int cond_value;
+
+/* Marked volatile so that different threads see the same value. This is
+ good enough in practice, although in theory stdatomic.h should be used. */
+static int volatile cond_value;
static pthread_cond_t condtest;
static pthread_mutex_t lockcond;
@@ -126,7 +129,10 @@ test_pthread_cond_wait ()
/*
* Timed Condition check
*/
-static int cond_timed_out;
+
+/* Marked volatile so that different threads see the same value. This is
+ good enough in practice, although in theory stdatomic.h should be used. */
+static int volatile cond_timed_out;
/* Stores in *TS the current time plus 1 second. */
static void
@@ -199,6 +205,7 @@ test_pthread_cond_timedwait (void)
abort ();
}
+
int
main ()
{
--
2.34.1
>From 08a66f5f3063dae0e79b71683e35f5123799db84 Mon Sep 17 00:00:00 2001
From: Bruno Haible <[email protected]>
Date: Fri, 28 Jun 2024 22:15:23 +0200
Subject: [PATCH 3/4] cond tests: Avoid theoretically possible failure.
* tests/test-cnd.c (cnd_wait_routine, cnd_timedwait_routine): Report to
the main thread if this thread comes too late.
(test_cnd_wait, test_cnd_timedwait): Return a 'skipped' value.
(main): Print SKIP instead of OK if the essence of the test was skipped.
* tests/test-cond.c (cond_routine, timedcond_routine): Report to the
main thread if this thread comes too late.
(test_cond, test_timedcond): Return a 'skipped' value.
(main): Print SKIP instead of OK if the essence of the test was skipped.
* tests/test-pthread-cond.c (pthread_cond_wait_routine,
pthread_cond_timedwait_routine): Report to the main thread if this
thread comes too late.
(test_pthread_cond_wait, test_pthread_cond_timedwait): Return a
'skipped' value.
(main): Print SKIP instead of OK if the essence of the test was skipped.
---
ChangeLog | 16 ++++++++++
tests/test-cnd.c | 61 ++++++++++++++++++++++++++++----------
tests/test-cond.c | 60 +++++++++++++++++++++++++++----------
tests/test-pthread-cond.c | 62 +++++++++++++++++++++++++++++----------
4 files changed, 154 insertions(+), 45 deletions(-)
diff --git a/ChangeLog b/ChangeLog
index 75f9d19a43..cb730bb006 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,21 @@
2024-06-28 Bruno Haible <[email protected]>
+ cond tests: Avoid theoretically possible failure.
+ * tests/test-cnd.c (cnd_wait_routine, cnd_timedwait_routine): Report to
+ the main thread if this thread comes too late.
+ (test_cnd_wait, test_cnd_timedwait): Return a 'skipped' value.
+ (main): Print SKIP instead of OK if the essence of the test was skipped.
+ * tests/test-cond.c (cond_routine, timedcond_routine): Report to the
+ main thread if this thread comes too late.
+ (test_cond, test_timedcond): Return a 'skipped' value.
+ (main): Print SKIP instead of OK if the essence of the test was skipped.
+ * tests/test-pthread-cond.c (pthread_cond_wait_routine,
+ pthread_cond_timedwait_routine): Report to the main thread if this
+ thread comes too late.
+ (test_pthread_cond_wait, test_pthread_cond_timedwait): Return a
+ 'skipped' value.
+ (main): Print SKIP instead of OK if the essence of the test was skipped.
+
cond tests: Improve multithread-safety.
* tests/test-cnd.c (cond_value, cond_timed_out): Mark as volatile.
* tests/test-cond.c (cond_value, cond_timed_out): Likewise.
diff --git a/tests/test-cnd.c b/tests/test-cnd.c
index 730c879c49..b8906a15c0 100644
--- a/tests/test-cnd.c
+++ b/tests/test-cnd.c
@@ -70,9 +70,19 @@ static int
cnd_wait_routine (void *arg)
{
ASSERT (mtx_lock (&lockcond) == thrd_success);
- while (!cond_value)
+ if (cond_value)
{
- ASSERT (cnd_wait (&condtest, &lockcond) == thrd_success);
+ /* The main thread already slept, and nevertheless this thread comes
+ too late. */
+ *(int *)arg = 1;
+ }
+ else
+ {
+ do
+ {
+ ASSERT (cnd_wait (&condtest, &lockcond) == thrd_success);
+ }
+ while (!cond_value);
}
ASSERT (mtx_unlock (&lockcond) == thrd_success);
@@ -81,16 +91,17 @@ cnd_wait_routine (void *arg)
return 0;
}
-static void
+static int
test_cnd_wait ()
{
+ int skipped = 0;
thrd_t thread;
int ret;
cond_value = 0;
/* Create a separate thread. */
- ASSERT (thrd_create (&thread, cnd_wait_routine, NULL) == thrd_success);
+ ASSERT (thrd_create (&thread, cnd_wait_routine, &skipped) == thrd_success);
/* Sleep for 2 seconds. */
{
@@ -118,6 +129,8 @@ test_cnd_wait ()
if (cond_value != 2)
abort ();
+
+ return skipped;
}
@@ -148,28 +161,40 @@ cnd_timedwait_routine (void *arg)
struct timespec ts;
ASSERT (mtx_lock (&lockcond) == thrd_success);
- while (!cond_value)
+ if (cond_value)
+ {
+ /* The main thread already slept, and nevertheless this thread comes
+ too late. */
+ *(int *)arg = 1;
+ }
+ else
{
- get_ts (&ts);
- ret = cnd_timedwait (&condtest, &lockcond, &ts);
- if (ret == thrd_timedout)
- cond_timed_out = 1;
+ do
+ {
+ get_ts (&ts);
+ ret = cnd_timedwait (&condtest, &lockcond, &ts);
+ if (ret == thrd_timedout)
+ cond_timed_out = 1;
+ }
+ while (!cond_value);
}
ASSERT (mtx_unlock (&lockcond) == thrd_success);
return 0;
}
-static void
+static int
test_cnd_timedwait (void)
{
+ int skipped = 0;
thrd_t thread;
int ret;
cond_value = cond_timed_out = 0;
/* Create a separate thread. */
- ASSERT (thrd_create (&thread, cnd_timedwait_routine, NULL) == thrd_success);
+ ASSERT (thrd_create (&thread, cnd_timedwait_routine, &skipped)
+ == thrd_success);
/* Sleep for 2 seconds. */
{
@@ -197,6 +222,8 @@ test_cnd_timedwait (void)
if (!cond_timed_out)
abort ();
+
+ return skipped;
}
@@ -216,13 +243,17 @@ main ()
#if DO_TEST_COND
printf ("Starting test_cnd_wait ..."); fflush (stdout);
- test_cnd_wait ();
- printf (" OK\n"); fflush (stdout);
+ {
+ int skipped = test_cnd_wait ();
+ printf (skipped ? " SKIP\n" : " OK\n"); fflush (stdout);
+ }
#endif
#if DO_TEST_TIMEDCOND
printf ("Starting test_cnd_timedwait ..."); fflush (stdout);
- test_cnd_timedwait ();
- printf (" OK\n"); fflush (stdout);
+ {
+ int skipped = test_cnd_timedwait ();
+ printf (skipped ? " SKIP\n" : " OK\n"); fflush (stdout);
+ }
#endif
return test_exit_status;
diff --git a/tests/test-cond.c b/tests/test-cond.c
index 80d696a5d8..712220aaac 100644
--- a/tests/test-cond.c
+++ b/tests/test-cond.c
@@ -70,9 +70,19 @@ static void *
cond_routine (void *arg)
{
gl_lock_lock (lockcond);
- while (!cond_value)
+ if (cond_value)
{
- gl_cond_wait (condtest, lockcond);
+ /* The main thread already slept, and nevertheless this thread comes
+ too late. */
+ *(int *)arg = 1;
+ }
+ else
+ {
+ do
+ {
+ gl_cond_wait (condtest, lockcond);
+ }
+ while (!cond_value);
}
gl_lock_unlock (lockcond);
@@ -81,15 +91,16 @@ cond_routine (void *arg)
return NULL;
}
-static void
+static int
test_cond ()
{
+ int skipped = 0;
gl_thread_t thread;
cond_value = 0;
/* Create a separate thread. */
- thread = gl_thread_create (cond_routine, NULL);
+ thread = gl_thread_create (cond_routine, &skipped);
/* Sleep for 2 seconds. */
{
@@ -113,6 +124,8 @@ test_cond ()
if (cond_value != 2)
abort ();
+
+ return skipped;
}
@@ -143,27 +156,38 @@ timedcond_routine (void *arg)
struct timespec ts;
gl_lock_lock (lockcond);
- while (!cond_value)
+ if (cond_value)
+ {
+ /* The main thread already slept, and nevertheless this thread comes
+ too late. */
+ *(int *)arg = 1;
+ }
+ else
{
- get_ts (&ts);
- ret = glthread_cond_timedwait (&condtest, &lockcond, &ts);
- if (ret == ETIMEDOUT)
- cond_timed_out = 1;
+ do
+ {
+ get_ts (&ts);
+ ret = glthread_cond_timedwait (&condtest, &lockcond, &ts);
+ if (ret == ETIMEDOUT)
+ cond_timed_out = 1;
+ }
+ while (!cond_value);
}
gl_lock_unlock (lockcond);
return NULL;
}
-static void
+static int
test_timedcond (void)
{
+ int skipped = 0;
gl_thread_t thread;
cond_value = cond_timed_out = 0;
/* Create a separate thread. */
- thread = gl_thread_create (timedcond_routine, NULL);
+ thread = gl_thread_create (timedcond_routine, &skipped);
/* Sleep for 2 seconds. */
{
@@ -187,6 +211,8 @@ test_timedcond (void)
if (!cond_timed_out)
abort ();
+
+ return skipped;
}
@@ -195,13 +221,17 @@ main ()
{
#if DO_TEST_COND
printf ("Starting test_cond ..."); fflush (stdout);
- test_cond ();
- printf (" OK\n"); fflush (stdout);
+ {
+ int skipped = test_cond ();
+ printf (skipped ? " SKIP\n" : " OK\n"); fflush (stdout);
+ }
#endif
#if DO_TEST_TIMEDCOND
printf ("Starting test_timedcond ..."); fflush (stdout);
- test_timedcond ();
- printf (" OK\n"); fflush (stdout);
+ {
+ int skipped = test_timedcond ();
+ printf (skipped ? " SKIP\n" : " OK\n"); fflush (stdout);
+ }
#endif
return 0;
diff --git a/tests/test-pthread-cond.c b/tests/test-pthread-cond.c
index 98c291c24c..3854ee2745 100644
--- a/tests/test-pthread-cond.c
+++ b/tests/test-pthread-cond.c
@@ -75,9 +75,19 @@ static void *
pthread_cond_wait_routine (void *arg)
{
ASSERT (pthread_mutex_lock (&lockcond) == 0);
- while (!cond_value)
+ if (cond_value)
{
- ASSERT (pthread_cond_wait (&condtest, &lockcond) == 0);
+ /* The main thread already slept, and nevertheless this thread comes
+ too late. */
+ *(int *)arg = 1;
+ }
+ else
+ {
+ do
+ {
+ ASSERT (pthread_cond_wait (&condtest, &lockcond) == 0);
+ }
+ while (!cond_value);
}
ASSERT (pthread_mutex_unlock (&lockcond) == 0);
@@ -86,16 +96,18 @@ pthread_cond_wait_routine (void *arg)
return NULL;
}
-static void
+static int
test_pthread_cond_wait ()
{
+ int skipped = 0;
pthread_t thread;
int ret;
cond_value = 0;
/* Create a separate thread. */
- ASSERT (pthread_create (&thread, NULL, pthread_cond_wait_routine, NULL) == 0);
+ ASSERT (pthread_create (&thread, NULL, pthread_cond_wait_routine, &skipped)
+ == 0);
/* Sleep for 2 seconds. */
{
@@ -123,6 +135,8 @@ test_pthread_cond_wait ()
if (cond_value != 2)
abort ();
+
+ return skipped;
}
@@ -153,28 +167,40 @@ pthread_cond_timedwait_routine (void *arg)
struct timespec ts;
ASSERT (pthread_mutex_lock (&lockcond) == 0);
- while (!cond_value)
+ if (cond_value)
+ {
+ /* The main thread already slept, and nevertheless this thread comes
+ too late. */
+ *(int *)arg = 1;
+ }
+ else
{
- get_ts (&ts);
- ret = pthread_cond_timedwait (&condtest, &lockcond, &ts);
- if (ret == ETIMEDOUT)
- cond_timed_out = 1;
+ do
+ {
+ get_ts (&ts);
+ ret = pthread_cond_timedwait (&condtest, &lockcond, &ts);
+ if (ret == ETIMEDOUT)
+ cond_timed_out = 1;
+ }
+ while (!cond_value);
}
ASSERT (pthread_mutex_unlock (&lockcond) == 0);
return NULL;
}
-static void
+static int
test_pthread_cond_timedwait (void)
{
+ int skipped = 0;
pthread_t thread;
int ret;
cond_value = cond_timed_out = 0;
/* Create a separate thread. */
- ASSERT (pthread_create (&thread, NULL, pthread_cond_timedwait_routine, NULL)
+ ASSERT (pthread_create (&thread, NULL,
+ pthread_cond_timedwait_routine, &skipped)
== 0);
/* Sleep for 2 seconds. */
@@ -203,6 +229,8 @@ test_pthread_cond_timedwait (void)
if (!cond_timed_out)
abort ();
+
+ return skipped;
}
@@ -230,13 +258,17 @@ main ()
#if DO_TEST_COND
printf ("Starting test_pthread_cond_wait ..."); fflush (stdout);
- test_pthread_cond_wait ();
- printf (" OK\n"); fflush (stdout);
+ {
+ int skipped = test_pthread_cond_wait ();
+ printf (skipped ? " SKIP\n" : " OK\n"); fflush (stdout);
+ }
#endif
#if DO_TEST_TIMEDCOND
printf ("Starting test_pthread_cond_timedwait ..."); fflush (stdout);
- test_pthread_cond_timedwait ();
- printf (" OK\n"); fflush (stdout);
+ {
+ int skipped = test_pthread_cond_timedwait ();
+ printf (skipped ? " SKIP\n" : " OK\n"); fflush (stdout);
+ }
#endif
return test_exit_status;
--
2.34.1
>From 80025a43e2a777e184e7522dd730ed9f02b3efe4 Mon Sep 17 00:00:00 2001
From: Bruno Haible <[email protected]>
Date: Sat, 29 Jun 2024 03:08:33 +0200
Subject: [PATCH 4/4] doc: Mention pthread_cond_timedwait bug caused by
hypervisor.
* doc/posix-functions/pthread_cond_timedwait.texi: Mention VirtualBox
specific bug.
* doc/posix-functions/cnd_timedwait.texi: Likewise.
---
ChangeLog | 7 +++++++
doc/posix-functions/cnd_timedwait.texi | 5 +++++
doc/posix-functions/pthread_cond_timedwait.texi | 5 +++++
3 files changed, 17 insertions(+)
diff --git a/ChangeLog b/ChangeLog
index cb730bb006..effda3a4a9 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+2024-06-28 Bruno Haible <[email protected]>
+
+ doc: Mention pthread_cond_timedwait bug caused by hypervisor.
+ * doc/posix-functions/pthread_cond_timedwait.texi: Mention VirtualBox
+ specific bug.
+ * doc/posix-functions/cnd_timedwait.texi: Likewise.
+
2024-06-28 Bruno Haible <[email protected]>
cond tests: Avoid theoretically possible failure.
diff --git a/doc/posix-functions/cnd_timedwait.texi b/doc/posix-functions/cnd_timedwait.texi
index 4e259835c1..3459161480 100644
--- a/doc/posix-functions/cnd_timedwait.texi
+++ b/doc/posix-functions/cnd_timedwait.texi
@@ -21,4 +21,9 @@
Portability problems not fixed by Gnulib:
@itemize
+@item
+This function sometimes waits indefinitely
+instead of up to the specified time point,
+on some platforms (glibc/Linux, musl libc) in some hypervisors:
+VirtualBox 6.1.50_Ubuntu with paravirtualization set to "Default" or "KVM".
@end itemize
diff --git a/doc/posix-functions/pthread_cond_timedwait.texi b/doc/posix-functions/pthread_cond_timedwait.texi
index 4662d4c2f7..ce849b4115 100644
--- a/doc/posix-functions/pthread_cond_timedwait.texi
+++ b/doc/posix-functions/pthread_cond_timedwait.texi
@@ -17,4 +17,9 @@
Portability problems not fixed by Gnulib:
@itemize
+@item
+This function sometimes waits indefinitely
+instead of up to the specified time point,
+on some platforms (glibc/Linux, musl libc) in some hypervisors:
+VirtualBox 6.1.50_Ubuntu with paravirtualization set to "Default" or "KVM".
@end itemize
--
2.34.1