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 <br...@clisp.org> 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 <br...@clisp.org> 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 <br...@clisp.org> 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 <br...@clisp.org> + + 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 <br...@clisp.org> 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 <br...@clisp.org> 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 <br...@clisp.org> + 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 <br...@clisp.org> 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 <br...@clisp.org> + 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 <br...@clisp.org> 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 <br...@clisp.org> + + 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 <br...@clisp.org> 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