summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLars Ingebrigtsen <larsi@gnus.org>2022-03-04 16:27:10 +0100
committerLars Ingebrigtsen <larsi@gnus.org>2022-03-04 16:27:10 +0100
commitcdbc2f9d274a23bcf6cb03046b1e5b4bdcedafb1 (patch)
treeaef3f6a300ddbfe0a55364f0dc310e27a391dbdc
parent345c4c6532d3784eed5acbaea8a78ce3aad071e4 (diff)
downloademacs-cdbc2f9d274a23bcf6cb03046b1e5b4bdcedafb1.tar.gz
Add some sleeps to gnutls_try_handshake
* admin/merge-gnulib (GNULIB_MODULES): Add the nanosleep module. * m4/gnulib-comp.m4 (gl_EARLY): * lib/gnulib.mk.in: Automatic update. * m4/nanosleep.m4: * lib/nanosleep.c: New module. * nt/mingw-cfg.site (gl_cv_func_free_preserves_errno): * nt/gnulib-cfg.mk (OMIT_GNULIB_MODULE_nanosleep): Omit nanosleep, since mingw has it. * src/gnutls.c (gnutls_try_handshake): Add some sleeping to the busy-wait loop so that we don't use 100% CPU here (bug#32452).
-rwxr-xr-xadmin/merge-gnulib2
-rw-r--r--lib/gnulib.mk.in13
-rw-r--r--lib/nanosleep.c195
-rw-r--r--m4/gnulib-comp.m47
-rw-r--r--m4/nanosleep.m4139
-rw-r--r--nt/gnulib-cfg.mk1
-rw-r--r--nt/mingw-cfg.site3
-rw-r--r--src/gnutls.c4
8 files changed, 363 insertions, 1 deletions
diff --git a/admin/merge-gnulib b/admin/merge-gnulib
index fec469c0179..ea3d23686f4 100755
--- a/admin/merge-gnulib
+++ b/admin/merge-gnulib
@@ -40,7 +40,7 @@ GNULIB_MODULES='
getloadavg getopt-gnu getrandom gettime gettimeofday gitlog-to-changelog
ieee754-h ignore-value intprops largefile libgmp lstat
manywarnings memmem-simple mempcpy memrchr minmax mkostemp mktime
- nproc nstrftime
+ nanosleep nproc nstrftime
pathmax pipe2 pselect pthread_sigmask
qcopy-acl readlink readlinkat regex
sig2str sigdescr_np socklen stat-time std-gnu11 stdalign stddef stdio
diff --git a/lib/gnulib.mk.in b/lib/gnulib.mk.in
index 3a9f5b9818e..3deeca98bef 100644
--- a/lib/gnulib.mk.in
+++ b/lib/gnulib.mk.in
@@ -129,6 +129,7 @@
# minmax \
# mkostemp \
# mktime \
+# nanosleep \
# nproc \
# nstrftime \
# pathmax \
@@ -207,6 +208,7 @@ CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CRYPTOLIB = @CRYPTOLIB@
CXX = @CXX@
+CXXCPP = @CXXCPP@
CXXFLAGS = @CXXFLAGS@
CYGWIN_OBJ = @CYGWIN_OBJ@
C_SWITCH_MACHINE = @C_SWITCH_MACHINE@
@@ -283,6 +285,7 @@ GL_COND_OBJ_MEMPCPY_CONDITION = @GL_COND_OBJ_MEMPCPY_CONDITION@
GL_COND_OBJ_MEMRCHR_CONDITION = @GL_COND_OBJ_MEMRCHR_CONDITION@
GL_COND_OBJ_MINI_GMP_GNULIB_CONDITION = @GL_COND_OBJ_MINI_GMP_GNULIB_CONDITION@
GL_COND_OBJ_MKOSTEMP_CONDITION = @GL_COND_OBJ_MKOSTEMP_CONDITION@
+GL_COND_OBJ_NANOSLEEP_CONDITION = @GL_COND_OBJ_NANOSLEEP_CONDITION@
GL_COND_OBJ_OPEN_CONDITION = @GL_COND_OBJ_OPEN_CONDITION@
GL_COND_OBJ_PSELECT_CONDITION = @GL_COND_OBJ_PSELECT_CONDITION@
GL_COND_OBJ_PTHREAD_SIGMASK_CONDITION = @GL_COND_OBJ_PTHREAD_SIGMASK_CONDITION@
@@ -2497,6 +2500,16 @@ EXTRA_libgnu_a_SOURCES += mktime.c
endif
## end gnulib module mktime-internal
+## begin gnulib module nanosleep
+ifeq (,$(OMIT_GNULIB_MODULE_nanosleep))
+
+ifneq (,$(GL_COND_OBJ_NANOSLEEP_CONDITION))
+libgnu_a_SOURCES += nanosleep.c
+endif
+
+endif
+## end gnulib module nanosleep
+
## begin gnulib module nproc
ifeq (,$(OMIT_GNULIB_MODULE_nproc))
diff --git a/lib/nanosleep.c b/lib/nanosleep.c
new file mode 100644
index 00000000000..446794edc0b
--- /dev/null
+++ b/lib/nanosleep.c
@@ -0,0 +1,195 @@
+/* Provide a replacement for the POSIX nanosleep function.
+
+ Copyright (C) 1999-2000, 2002, 2004-2022 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 Jim Meyering
+ and Bruno Haible for the native Windows part */
+
+#include <config.h>
+
+#include <time.h>
+
+#include "intprops.h"
+#include "verify.h"
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/select.h>
+#include <signal.h>
+
+#include <errno.h>
+
+#include <unistd.h>
+
+
+enum { BILLION = 1000 * 1000 * 1000 };
+
+#if HAVE_BUG_BIG_NANOSLEEP
+
+int
+nanosleep (const struct timespec *requested_delay,
+ struct timespec *remaining_delay)
+# undef nanosleep
+{
+ /* nanosleep mishandles large sleeps due to internal overflow problems.
+ The worst known case of this is Linux 2.6.9 with glibc 2.3.4, which
+ can't sleep more than 24.85 days (2^31 milliseconds). Similarly,
+ cygwin 1.5.x, which can't sleep more than 49.7 days (2^32 milliseconds).
+ Solve this by breaking the sleep up into smaller chunks. */
+
+ if (requested_delay->tv_nsec < 0 || BILLION <= requested_delay->tv_nsec)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+
+ {
+ /* Verify that time_t is large enough. */
+ verify (TYPE_MAXIMUM (time_t) / 24 / 24 / 60 / 60);
+ const time_t limit = 24 * 24 * 60 * 60;
+ time_t seconds = requested_delay->tv_sec;
+ struct timespec intermediate;
+ intermediate.tv_nsec = requested_delay->tv_nsec;
+
+ while (limit < seconds)
+ {
+ int result;
+ intermediate.tv_sec = limit;
+ result = nanosleep (&intermediate, remaining_delay);
+ seconds -= limit;
+ if (result)
+ {
+ if (remaining_delay)
+ remaining_delay->tv_sec += seconds;
+ return result;
+ }
+ intermediate.tv_nsec = 0;
+ }
+ intermediate.tv_sec = seconds;
+ return nanosleep (&intermediate, remaining_delay);
+ }
+}
+
+#elif defined _WIN32 && ! defined __CYGWIN__
+/* Native Windows platforms. */
+
+# define WIN32_LEAN_AND_MEAN
+# include <windows.h>
+
+/* The Windows API function Sleep() has a resolution of about 15 ms and takes
+ at least 5 ms to execute. We use this function for longer time periods.
+ Additionally, we use busy-looping over short time periods, to get a
+ resolution of about 0.01 ms. In order to measure such short timespans,
+ we use the QueryPerformanceCounter() function. */
+
+int
+nanosleep (const struct timespec *requested_delay,
+ struct timespec *remaining_delay)
+{
+ static bool initialized;
+ /* Number of performance counter increments per nanosecond,
+ or zero if it could not be determined. */
+ static double ticks_per_nanosecond;
+
+ if (requested_delay->tv_nsec < 0 || BILLION <= requested_delay->tv_nsec)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* For requested delays of one second or more, 15ms resolution is
+ sufficient. */
+ if (requested_delay->tv_sec == 0)
+ {
+ if (!initialized)
+ {
+ /* Initialize ticks_per_nanosecond. */
+ LARGE_INTEGER ticks_per_second;
+
+ if (QueryPerformanceFrequency (&ticks_per_second))
+ ticks_per_nanosecond =
+ (double) ticks_per_second.QuadPart / 1000000000.0;
+
+ initialized = true;
+ }
+ if (ticks_per_nanosecond)
+ {
+ /* QueryPerformanceFrequency worked. We can use
+ QueryPerformanceCounter. Use a combination of Sleep and
+ busy-looping. */
+ /* Number of milliseconds to pass to the Sleep function.
+ Since Sleep can take up to 8 ms less or 8 ms more than requested
+ (or maybe more if the system is loaded), we subtract 10 ms. */
+ int sleep_millis = (int) requested_delay->tv_nsec / 1000000 - 10;
+ /* Determine how many ticks to delay. */
+ LONGLONG wait_ticks = requested_delay->tv_nsec * ticks_per_nanosecond;
+ /* Start. */
+ LARGE_INTEGER counter_before;
+ if (QueryPerformanceCounter (&counter_before))
+ {
+ /* Wait until the performance counter has reached this value.
+ We don't need to worry about overflow, because the performance
+ counter is reset at reboot, and with a frequency of 3.6E6
+ ticks per second 63 bits suffice for over 80000 years. */
+ LONGLONG wait_until = counter_before.QuadPart + wait_ticks;
+ /* Use Sleep for the longest part. */
+ if (sleep_millis > 0)
+ Sleep (sleep_millis);
+ /* Busy-loop for the rest. */
+ for (;;)
+ {
+ LARGE_INTEGER counter_after;
+ if (!QueryPerformanceCounter (&counter_after))
+ /* QueryPerformanceCounter failed, but succeeded earlier.
+ Should not happen. */
+ break;
+ if (counter_after.QuadPart >= wait_until)
+ /* The requested time has elapsed. */
+ break;
+ }
+ goto done;
+ }
+ }
+ }
+ /* Implementation for long delays and as fallback. */
+ Sleep (requested_delay->tv_sec * 1000 + requested_delay->tv_nsec / 1000000);
+
+ done:
+ /* Sleep is not interruptible. So there is no remaining delay. */
+ if (remaining_delay != NULL)
+ {
+ remaining_delay->tv_sec = 0;
+ remaining_delay->tv_nsec = 0;
+ }
+ return 0;
+}
+
+#else
+/* Other platforms lacking nanosleep.
+ It's not clear whether these are still practical porting targets.
+ For now, just fall back on pselect. */
+
+/* Suspend execution for at least *REQUESTED_DELAY seconds. The
+ *REMAINING_DELAY part isn't implemented yet. */
+
+int
+nanosleep (const struct timespec *requested_delay,
+ struct timespec *remaining_delay)
+{
+ return pselect (0, NULL, NULL, NULL, requested_delay, NULL);
+}
+#endif
diff --git a/m4/gnulib-comp.m4 b/m4/gnulib-comp.m4
index 1d31239d2db..fb5f1b52a43 100644
--- a/m4/gnulib-comp.m4
+++ b/m4/gnulib-comp.m4
@@ -140,6 +140,7 @@ AC_DEFUN([gl_EARLY],
# Code from module mktime:
# Code from module mktime-internal:
# Code from module multiarch:
+ # Code from module nanosleep:
# Code from module nocrash:
# Code from module nproc:
# Code from module nstrftime:
@@ -430,6 +431,10 @@ AC_DEFUN([gl_INIT],
fi
gl_TIME_MODULE_INDICATOR([mktime])
gl_MULTIARCH
+ gl_FUNC_NANOSLEEP
+ gl_CONDITIONAL([GL_COND_OBJ_NANOSLEEP],
+ [test $HAVE_NANOSLEEP = 0 || test $REPLACE_NANOSLEEP = 1])
+ gl_TIME_MODULE_INDICATOR([nanosleep])
gl_NPROC
gl_FUNC_GNU_STRFTIME
gl_PATHMAX
@@ -1304,6 +1309,7 @@ AC_DEFUN([gl_FILE_LIST], [
lib/mkostemp.c
lib/mktime-internal.h
lib/mktime.c
+ lib/nanosleep.c
lib/nproc.c
lib/nproc.h
lib/nstrftime.c
@@ -1456,6 +1462,7 @@ AC_DEFUN([gl_FILE_LIST], [
m4/mktime.m4
m4/mode_t.m4
m4/multiarch.m4
+ m4/nanosleep.m4
m4/nocrash.m4
m4/nproc.m4
m4/nstrftime.m4
diff --git a/m4/nanosleep.m4 b/m4/nanosleep.m4
new file mode 100644
index 00000000000..1964b1ea47d
--- /dev/null
+++ b/m4/nanosleep.m4
@@ -0,0 +1,139 @@
+# serial 41
+
+dnl From Jim Meyering.
+dnl Check for the nanosleep function.
+dnl If not found, use the supplied replacement.
+dnl
+
+# Copyright (C) 1999-2001, 2003-2022 Free Software Foundation, Inc.
+
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+AC_DEFUN([gl_FUNC_NANOSLEEP],
+[
+ AC_REQUIRE([gl_TIME_H_DEFAULTS])
+ AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
+
+ dnl Persuade glibc and Solaris <time.h> to declare nanosleep.
+ AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS])
+
+ AC_CHECK_DECLS_ONCE([alarm])
+
+ nanosleep_save_libs=$LIBS
+
+ # Solaris 2.5.1 needs -lposix4 to get the nanosleep function.
+ # Solaris 7 prefers the library name -lrt to the obsolescent name -lposix4.
+ LIB_NANOSLEEP=
+ AC_SUBST([LIB_NANOSLEEP])
+ AC_SEARCH_LIBS([nanosleep], [rt posix4],
+ [test "$ac_cv_search_nanosleep" = "none required" ||
+ LIB_NANOSLEEP=$ac_cv_search_nanosleep])
+ if test "x$ac_cv_search_nanosleep" != xno; then
+ dnl The system has a nanosleep function.
+
+ AC_REQUIRE([gl_MULTIARCH])
+ if test $APPLE_UNIVERSAL_BUILD = 1; then
+ # A universal build on Apple Mac OS X platforms.
+ # The test result would be 'no (mishandles large arguments)' in 64-bit
+ # mode but 'yes' in 32-bit mode. But we need a configuration result that
+ # is valid in both modes.
+ gl_cv_func_nanosleep='no (mishandles large arguments)'
+ fi
+
+ AC_CACHE_CHECK([for working nanosleep],
+ [gl_cv_func_nanosleep],
+ [
+ AC_RUN_IFELSE(
+ [AC_LANG_SOURCE([[
+ #include <errno.h>
+ #include <limits.h>
+ #include <signal.h>
+ #include <time.h>
+ #include <unistd.h>
+ #define TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
+ #define TYPE_MAXIMUM(t) \
+ ((t) (! TYPE_SIGNED (t) \
+ ? (t) -1 \
+ : ((((t) 1 << (sizeof (t) * CHAR_BIT - 2)) - 1) * 2 + 1)))
+
+ #if HAVE_DECL_ALARM
+ static void
+ check_for_SIGALRM (int sig)
+ {
+ if (sig != SIGALRM)
+ _exit (1);
+ }
+ #endif
+
+ int
+ main ()
+ {
+ static struct timespec ts_sleep;
+ static struct timespec ts_remaining;
+ /* Test for major problems first. */
+ if (! nanosleep)
+ return 2;
+ ts_sleep.tv_sec = 0;
+ ts_sleep.tv_nsec = 1;
+ #if HAVE_DECL_ALARM
+ {
+ static struct sigaction act;
+ act.sa_handler = check_for_SIGALRM;
+ sigemptyset (&act.sa_mask);
+ sigaction (SIGALRM, &act, NULL);
+ alarm (1);
+ if (nanosleep (&ts_sleep, NULL) != 0)
+ return 3;
+ /* Test for a minor problem: the handling of large arguments. */
+ ts_sleep.tv_sec = TYPE_MAXIMUM (time_t);
+ ts_sleep.tv_nsec = 999999999;
+ alarm (1);
+ if (nanosleep (&ts_sleep, &ts_remaining) != -1)
+ return 4;
+ if (errno != EINTR)
+ return 5;
+ if (ts_remaining.tv_sec <= TYPE_MAXIMUM (time_t) - 10)
+ return 6;
+ }
+ #else /* A simpler test for native Windows. */
+ if (nanosleep (&ts_sleep, &ts_remaining) < 0)
+ return 3;
+ #endif
+ return 0;
+ }]])],
+ [gl_cv_func_nanosleep=yes],
+ [case $? in dnl (
+ 4|5|6) gl_cv_func_nanosleep='no (mishandles large arguments)';; dnl (
+ *) gl_cv_func_nanosleep=no;;
+ esac],
+ [case "$host_os" in dnl ((
+ linux*) # Guess it halfway works when the kernel is Linux.
+ gl_cv_func_nanosleep='guessing no (mishandles large arguments)' ;;
+ mingw*) # Guess no on native Windows.
+ gl_cv_func_nanosleep='guessing no' ;;
+ *) # If we don't know, obey --enable-cross-guesses.
+ gl_cv_func_nanosleep="$gl_cross_guess_normal" ;;
+ esac
+ ])
+ ])
+ case "$gl_cv_func_nanosleep" in
+ *yes)
+ REPLACE_NANOSLEEP=0
+ ;;
+ *)
+ REPLACE_NANOSLEEP=1
+ case "$gl_cv_func_nanosleep" in
+ *"mishandles large arguments"*)
+ AC_DEFINE([HAVE_BUG_BIG_NANOSLEEP], [1],
+ [Define to 1 if nanosleep mishandles large arguments.])
+ ;;
+ esac
+ ;;
+ esac
+ else
+ HAVE_NANOSLEEP=0
+ fi
+ LIBS=$nanosleep_save_libs
+])
diff --git a/nt/gnulib-cfg.mk b/nt/gnulib-cfg.mk
index 4748474f1dc..69119b135e2 100644
--- a/nt/gnulib-cfg.mk
+++ b/nt/gnulib-cfg.mk
@@ -74,3 +74,4 @@ OMIT_GNULIB_MODULE_futimens = true
OMIT_GNULIB_MODULE_utimensat = true
OMIT_GNULIB_MODULE_file-has-acl = true
OMIT_GNULIB_MODULE_nproc = true
+OMIT_GNULIB_MODULE_nanosleep = true
diff --git a/nt/mingw-cfg.site b/nt/mingw-cfg.site
index 6ab81e943f1..7ca19cbad06 100644
--- a/nt/mingw-cfg.site
+++ b/nt/mingw-cfg.site
@@ -167,3 +167,6 @@ ac_cv_func_strsignal=no
# implementation of 'free' doesn't touch errno, and it emits a
# compilation warning.
gl_cv_func_free_preserves_errno=yes
+# Don't build the Gnulib nanosleep module: it requires W2K or later,
+# and MinGW does have nanosleep.
+gl_cv_func_nanosleep=yes
diff --git a/src/gnutls.c b/src/gnutls.c
index 3ec38370679..09590ca005c 100644
--- a/src/gnutls.c
+++ b/src/gnutls.c
@@ -616,6 +616,9 @@ gnutls_try_handshake (struct Lisp_Process *proc)
gnutls_session_t state = proc->gnutls_state;
int ret;
bool non_blocking = proc->is_non_blocking_client;
+ /* Sleep for ten milliseconds when busy-looping in
+ gnutls_handshake. */
+ struct timespec delay = { 0, 1000 * 1000 * 10 };
if (proc->gnutls_complete_negotiation_p)
non_blocking = false;
@@ -630,6 +633,7 @@ gnutls_try_handshake (struct Lisp_Process *proc)
maybe_quit ();
if (non_blocking && ret != GNUTLS_E_INTERRUPTED)
break;
+ nanosleep (&delay, NULL);
}
proc->gnutls_initstage = GNUTLS_STAGE_HANDSHAKE_TRIED;