[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: a pthread_cond_timedwait bug
From: |
Bruno Haible |
Subject: |
Re: a pthread_cond_timedwait bug |
Date: |
Sat, 29 Jun 2024 16:06:47 +0200 |
I wrote:
> 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)
The test-pthread-cond, test-cond, test-cnd tests are not the only ones that
fail in this VM configuration. test-pthread_sigmask[12] and test-sigprocmask
occasionally fail as well. Both in glibc and in musl libc systems.
This patch avoids the test failures.
2024-06-29 Bruno Haible <bruno@clisp.org>
tests: Avoid test failures due to VirtualBox specific bug.
* tests/virtualbox.h: New file.
* tests/test-pthread-cond.c: Include virtualbox.h.
(main): Skip the test under VirtualBox with KVM paravirtualization and
more than 1 CPU.
* tests/test-cnd.c: Include virtualbox.h.
(main): Skip the test under VirtualBox with KVM paravirtualization and
more than 1 CPU.
* tests/test-cond.c: Include virtualbox.h.
(main): Skip the test under VirtualBox with KVM paravirtualization and
more than 1 CPU.
* tests/test-pthread_sigmask1.c: Include virtualbox.h.
(main): Skip the test under VirtualBox with KVM paravirtualization and
more than 1 CPU.
* tests/test-pthread_sigmask2.c: Include virtualbox.h.
(main): Skip the test under VirtualBox with KVM paravirtualization and
more than 1 CPU.
* tests/test-sigprocmask.c: Include virtualbox.h.
(main): Skip the test under VirtualBox with KVM paravirtualization and
more than 1 CPU.
* modules/pthread-cond-tests (Files): Add tests/virtualbox.h.
* modules/cnd-tests (Files): Likewise.
* modules/cond-tests (Files): Likewise.
* modules/pthread_sigmask-tests (Files): Likewise.
* modules/sigprocmask-tests (Files): Likewise.
diff --git a/modules/cnd-tests b/modules/cnd-tests
index f9dc79ea7d..be3c9de0a6 100644
--- a/modules/cnd-tests
+++ b/modules/cnd-tests
@@ -1,5 +1,6 @@
Files:
tests/test-cnd.c
+tests/virtualbox.h
tests/macros.h
Depends-on:
diff --git a/modules/cond-tests b/modules/cond-tests
index 6b4c45e2c0..d984ca84b1 100644
--- a/modules/cond-tests
+++ b/modules/cond-tests
@@ -1,5 +1,6 @@
Files:
tests/test-cond.c
+tests/virtualbox.h
Depends-on:
lock
diff --git a/modules/pthread-cond-tests b/modules/pthread-cond-tests
index fc0a1c8e06..baafb48993 100644
--- a/modules/pthread-cond-tests
+++ b/modules/pthread-cond-tests
@@ -1,5 +1,6 @@
Files:
tests/test-pthread-cond.c
+tests/virtualbox.h
tests/macros.h
Depends-on:
diff --git a/modules/pthread_sigmask-tests b/modules/pthread_sigmask-tests
index 00185a0d3e..508c02a16f 100644
--- a/modules/pthread_sigmask-tests
+++ b/modules/pthread_sigmask-tests
@@ -2,6 +2,7 @@ Files:
tests/test-pthread_sigmask1.c
tests/test-pthread_sigmask2.c
tests/signature.h
+tests/virtualbox.h
tests/macros.h
Depends-on:
diff --git a/modules/sigprocmask-tests b/modules/sigprocmask-tests
index 8e6523e785..7880b64b9b 100644
--- a/modules/sigprocmask-tests
+++ b/modules/sigprocmask-tests
@@ -1,6 +1,7 @@
Files:
tests/test-sigprocmask.c
tests/signature.h
+tests/virtualbox.h
tests/macros.h
Depends-on:
diff --git a/tests/test-cnd.c b/tests/test-cnd.c
index b8906a15c0..bcd0e7786c 100644
--- a/tests/test-cnd.c
+++ b/tests/test-cnd.c
@@ -41,6 +41,7 @@
# include <unistd.h>
#endif
+#include "virtualbox.h"
#include "macros.h"
#if ENABLE_DEBUGGING
@@ -230,6 +231,16 @@ test_cnd_timedwait (void)
int
main ()
{
+ /* This test occasionally fails on Linux (glibc or musl libc), in a
+ VirtualBox VM with paravirtualization = Default or KVM, with ≥ 2 CPUs.
+ Skip the test in this situation. */
+ if (is_running_under_virtualbox_kvm () && num_cpus () > 1)
+ {
+ fputs ("Skipping test: avoiding VirtualBox bug with KVM
paravirtualization\n",
+ stderr);
+ return 77;
+ }
+
#if HAVE_DECL_ALARM
/* Declare failure if test takes too long, by using default abort
caused by SIGALRM. */
diff --git a/tests/test-cond.c b/tests/test-cond.c
index 712220aaac..113dd392f6 100644
--- a/tests/test-cond.c
+++ b/tests/test-cond.c
@@ -43,6 +43,8 @@
#include "glthread/thread.h"
#include "glthread/yield.h"
+#include "virtualbox.h"
+
#if ENABLE_DEBUGGING
# define dbgprintf printf
#else
@@ -219,6 +221,16 @@ test_timedcond (void)
int
main ()
{
+ /* This test occasionally fails on Linux (glibc or musl libc), in a
+ VirtualBox VM with paravirtualization = Default or KVM, with ≥ 2 CPUs.
+ Skip the test in this situation. */
+ if (is_running_under_virtualbox_kvm () && num_cpus () > 1)
+ {
+ fputs ("Skipping test: avoiding VirtualBox bug with KVM
paravirtualization\n",
+ stderr);
+ return 77;
+ }
+
#if DO_TEST_COND
printf ("Starting test_cond ..."); fflush (stdout);
{
diff --git a/tests/test-pthread-cond.c b/tests/test-pthread-cond.c
index 3854ee2745..3f621fa17a 100644
--- a/tests/test-pthread-cond.c
+++ b/tests/test-pthread-cond.c
@@ -46,6 +46,7 @@
# include <unistd.h>
#endif
+#include "virtualbox.h"
#include "macros.h"
#if ENABLE_DEBUGGING
@@ -237,6 +238,16 @@ test_pthread_cond_timedwait (void)
int
main ()
{
+ /* This test occasionally fails on Linux (glibc or musl libc), in a
+ VirtualBox VM with paravirtualization = Default or KVM, with ≥ 2 CPUs.
+ Skip the test in this situation. */
+ if (is_running_under_virtualbox_kvm () && num_cpus () > 1)
+ {
+ fputs ("Skipping test: avoiding VirtualBox bug with KVM
paravirtualization\n",
+ stderr);
+ return 77;
+ }
+
#if HAVE_DECL_ALARM
/* Declare failure if test takes too long, by using default abort
caused by SIGALRM. */
diff --git a/tests/test-pthread_sigmask1.c b/tests/test-pthread_sigmask1.c
index 93ed7eb172..aa7efd65f4 100644
--- a/tests/test-pthread_sigmask1.c
+++ b/tests/test-pthread_sigmask1.c
@@ -29,6 +29,7 @@ SIGNATURE_CHECK (pthread_sigmask, int, (int, const sigset_t
*, sigset_t *));
#include <stdlib.h>
#include <unistd.h>
+#include "virtualbox.h"
#include "macros.h"
#if !(defined _WIN32 && !defined __CYGWIN__)
@@ -44,6 +45,16 @@ sigint_handler (_GL_UNUSED int sig)
int
main ()
{
+ /* This test occasionally fails on Linux (glibc or musl libc), in a
+ VirtualBox VM with paravirtualization = Default or KVM, with ≥ 2 CPUs.
+ Skip the test in this situation. */
+ if (is_running_under_virtualbox_kvm () && num_cpus () > 1)
+ {
+ fputs ("Skipping test: avoiding VirtualBox bug with KVM
paravirtualization\n",
+ stderr);
+ return 77;
+ }
+
sigset_t set;
intmax_t pid = getpid ();
char command[80];
diff --git a/tests/test-pthread_sigmask2.c b/tests/test-pthread_sigmask2.c
index fa3c1d8d91..a847e6b3b3 100644
--- a/tests/test-pthread_sigmask2.c
+++ b/tests/test-pthread_sigmask2.c
@@ -25,6 +25,7 @@
#include <stdio.h>
#include <unistd.h>
+#include "virtualbox.h"
#include "macros.h"
#if USE_POSIX_THREADS || USE_ISOC_AND_POSIX_THREADS
@@ -51,6 +52,16 @@ sigint_handler (_GL_UNUSED int sig)
int
main ()
{
+ /* This test occasionally fails on Linux (glibc or musl libc), in a
+ VirtualBox VM with paravirtualization = Default or KVM, with ≥ 2 CPUs.
+ Skip the test in this situation. */
+ if (is_running_under_virtualbox_kvm () && num_cpus () > 1)
+ {
+ fputs ("Skipping test: avoiding VirtualBox bug with KVM
paravirtualization\n",
+ stderr);
+ return 77;
+ }
+
sigset_t set;
signal (SIGINT, sigint_handler);
diff --git a/tests/test-sigprocmask.c b/tests/test-sigprocmask.c
index 30c04e5b72..b9cdb6b063 100644
--- a/tests/test-sigprocmask.c
+++ b/tests/test-sigprocmask.c
@@ -29,6 +29,7 @@ SIGNATURE_CHECK (sigprocmask, int, (int, const sigset_t *,
sigset_t *));
#include <stdlib.h>
#include <unistd.h>
+#include "virtualbox.h"
#include "macros.h"
#if !(defined _WIN32 && !defined __CYGWIN__)
@@ -44,6 +45,16 @@ sigint_handler (_GL_UNUSED int sig)
int
main ()
{
+ /* This test occasionally fails on Linux (glibc or musl libc), in a
+ VirtualBox VM with paravirtualization = Default or KVM, with ≥ 2 CPUs.
+ Skip the test in this situation. */
+ if (is_running_under_virtualbox_kvm () && num_cpus () > 1)
+ {
+ fputs ("Skipping test: avoiding VirtualBox bug with KVM
paravirtualization\n",
+ stderr);
+ return 77;
+ }
+
sigset_t set;
intmax_t pid = getpid ();
char command[80];
diff --git a/tests/virtualbox.h b/tests/virtualbox.h
new file mode 100644
index 0000000000..86a47fa98d
--- /dev/null
+++ b/tests/virtualbox.h
@@ -0,0 +1,134 @@
+/* Determine whether the current system is running under VirtualBox/KVM.
+ Copyright (C) 2021-2024 Free Software Foundation, Inc.
+
+ This file is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ This file is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>. */
+
+/* Written by Bruno Haible <bruno@clisp.org>, 2024. */
+
+#ifdef __linux__
+# include <fcntl.h>
+# include <string.h>
+# include <unistd.h>
+#endif
+
+/* This function determines whether the current system is Linux and running
+ under the VirtualBox emulator. */
+static bool
+is_running_under_virtualbox (void)
+{
+#ifdef __linux__
+ /* On distributions with systemd, this could be done through
+ test `systemd-detect-virt --vm` = oracle
+ More generally, it can be done through
+ test "`cat /sys/class/dmi/id/product_name`" = VirtualBox
+ This is what we do here. */
+ char buf[4096];
+ int fd = open ("/sys/class/dmi/id/product_name", O_RDONLY);
+ if (fd >= 0)
+ {
+ int n = read (fd, buf, sizeof (buf));
+ close (fd);
+ if (n == 10 + 1 && memcmp (buf, "VirtualBox\n", 10 + 1) == 0)
+ return true;
+ }
+#endif
+
+ return false;
+}
+
+/* This function determines whether the current system is Linux and running
+ under the VirtualBox emulator, with paravirtualization acceleration set to
+ "Default" or "KVM". */
+static bool
+is_running_under_virtualbox_kvm (void)
+{
+#ifdef __linux__
+ if (is_running_under_virtualbox ())
+ {
+ /* As root, one can determine this paravirtualization mode through
+ dmesg | grep -i kvm
+ which produces output like this:
+ [ 0.000000] Hypervisor detected: KVM
+ [ 0.000000] kvm-clock: Using msrs 4b564d01 and 4b564d00
+ [ 0.000001] kvm-clock: using sched offset of 3736655524 cycles
+ [ 0.000004] clocksource: kvm-clock: mask: 0xffffffffffffffff
max_cycles: 0x1cd42e4dffb, max_idle_ns: 881590591483 ns
+ [ 0.007355] Booting paravirtualized kernel on KVM
+ [ 0.213538] clocksource: Switched to clocksource kvm-clock
+ So, we test whether the file
+ /sys/devices/system/clocksource/clocksource0/available_clocksource
+ contains the word 'kvm-clock'. */
+ char buf[4096 + 1];
+ int fd = open
("/sys/devices/system/clocksource/clocksource0/available_clocksource",
O_RDONLY);
+ if (fd >= 0)
+ {
+ int n = read (fd, buf, sizeof (buf) - 1);
+ close (fd);
+ if (n > 0)
+ {
+ buf[n] = '\0';
+ char *saveptr;
+ char *word;
+ for (word = strtok_r (buf, " \n", &saveptr);
+ word != NULL;
+ word = strtok_r (NULL, " \n", &saveptr))
+ {
+ if (strcmp (word, "kvm-clock") == 0)
+ return true;
+ }
+ }
+ }
+ }
+#endif
+
+ return false;
+}
+
+/* This function returns the number of CPUs in the current system, assuming
+ it is Linux. */
+static int
+num_cpus (void)
+{
+#ifdef __linux__
+ /* We could use sysconf (_SC_NPROCESSORS_CONF), which on glibc and musl libc
+ is implemented through sched_getaffinity(). But there are some
+ complications; see nproc.c. It's simpler to parse /proc/cpuinfo.
+ More precisely, it's sufficient to count the number of blank lines in
+ /proc/cpuinfo. */
+ char buf[4096];
+ int fd = open ("/proc/cpuinfo", O_RDONLY);
+ if (fd >= 0)
+ {
+ unsigned int blank_lines = 0;
+ bool last_char_was_newline = false;
+ for (;;)
+ {
+ int n = read (fd, buf, sizeof (buf));
+ if (n <= 0)
+ break;
+ int i;
+ for (i = 0; i < n; i++)
+ {
+ if (last_char_was_newline && buf[i] == '\n')
+ blank_lines++;
+ last_char_was_newline = (buf[i] == '\n');
+ }
+ }
+ close (fd);
+ if (blank_lines > 0)
+ return blank_lines;
+ }
+#endif
+
+ return 1;
+}