File 8213-erts-configure-option-for-ensuring-monotonicity-of-O.patch of Package erlang

From 9ba3b326e5f3a25faa977f73314d1481fbdd7805 Mon Sep 17 00:00:00 2001
From: Rickard Green <rickard@erlang.org>
Date: Tue, 26 Mar 2024 17:52:12 +0100
Subject: [PATCH 3/3] [erts] configure option for ensuring monotonicity of OS
 monotonic time

---
 HOWTO/INSTALL.md                    |  8 +++
 erts/config.h.in                    |  4 ++
 erts/configure                      | 26 +++++++++
 erts/configure.in                   |  9 +++
 erts/emulator/beam/erl_lock_check.c |  6 +-
 erts/emulator/beam/erl_time_sup.c   | 87 ++++++++++++++++++++++++-----
 erts/emulator/beam/sys.h            |  4 ++
 make/configure                      | 18 ++++++
 make/configure.in                   |  4 ++
 9 files changed, 150 insertions(+), 16 deletions(-)

diff --git a/HOWTO/INSTALL.md b/HOWTO/INSTALL.md
index e7fb4ad27a..3c634f769a 100644
--- a/HOWTO/INSTALL.md
+++ b/HOWTO/INSTALL.md
@@ -420,6 +420,14 @@ Some of the available `configure` options are:
     time, and OS monotonic time with higher or lower resolution than chosen by
     default. Note that both alternatives may have a negative impact on the performance
     and scalability compared to the default clock sources chosen.
+*   `--enable-ensure-os-monotonic-time` - Enable functionality ensuring the
+    monotonicity of monotonic timestamps delivered by the OS. When a
+    non-monotonic timestamp is detected, it will be replaced by the last
+    delivered monotonic timestamp before being used by Erlang's time
+    functionality. Note that you do *not* want to enable this unless the OS
+    monotonic time source on the system fails to produce monotonic timestamps.
+    This since ensuring the monotonicity of OS monotonic timestamps will hurt
+    scalability and performance of the system.
 *   `--disable-saved-compile-time` - Disable saving of compile date and time
     in the emulator binary.
 *   `--enable-ei-dynamic-lib` - Make erl\_interface build a shared library in addition
diff --git a/erts/config.h.in b/erts/config.h.in
index 8295b90410..4e0cd90e60 100644
--- a/erts/config.h.in
+++ b/erts/config.h.in
@@ -51,6 +51,10 @@
 /* Define > 0 if big-endian < 0 if little-endian, or 0 if unknown */
 #undef ERTS_ENDIANNESS
 
+/* Define if ensurance of the monotonicity of OS monotonic timestamps should
+   be enabled */
+#undef ERTS_ENSURE_OS_MONOTONIC_TIME
+
 /* Define if OS monotonic clock is corrected */
 #undef ERTS_HAVE_CORRECTED_OS_MONOTONIC_TIME
 
diff --git a/erts/configure b/erts/configure
index 7da8d32a1b..312ed0b5a1 100755
--- a/erts/configure
+++ b/erts/configure
@@ -812,6 +812,7 @@ enable_vm_probes
 with_assumed_cache_line_size
 enable_systemd
 with_microstate_accounting
+enable_ensure_os_monotonic_time
 enable_static_nifs
 enable_static_drivers
 with_ets_write_concurrency_locks
@@ -1525,6 +1526,17 @@ Optional Features:
                           possible if --with-dynamic-trace is enabled, and
                           then default)
   --enable-systemd        enable systemd support in epmd
+  --enable-ensure-os-monotonic-time
+                          enable functionality ensuring the monotonicity of
+                          monotonic timestamps delivered by the OS. When a
+                          non-monotonic timestamp is detected, it will be
+                          replaced by the last delivered monotonic timestamp
+                          before being used by Erlang's time functionality.
+                          Note that you do *not* want to enable this unless
+                          the OS monotonic time source on the system fails to
+                          produce monotonic timestamps. This since ensuring
+                          the monotonicity of OS monotonic timestamps will
+                          hurt scalability and performance of the system.
   --disable-saved-compile-time
                           disable saved compile time
   --enable-static-nifs    link nifs statically. If yes then all nifs in all
@@ -4193,6 +4205,20 @@ $as_echo "#define ERTS_ENABLE_MSACC 2" >>confdefs.h
     *) ;;
 esac
 
+# Check whether --enable-ensure-os-monotonic-time was given.
+if test "${enable_ensure_os_monotonic_time+set}" = set; then :
+  enableval=$enable_ensure_os_monotonic_time;
+fi
+
+
+if test "$enable_ensure_os_monotonic_time" = "yes"; then :
+
+
+$as_echo "#define ERTS_ENSURE_OS_MONOTONIC_TIME 1" >>confdefs.h
+
+
+fi
+
 OTP_RELEASE=
 if test "${ERLANG_COMMERCIAL_BUILD}" != ""; then
 	OTP_EXTRA_FLAGS=-DOTP_RELEASE
diff --git a/erts/configure.in b/erts/configure.in
index beff4275f8..fecf9c901f 100644
--- a/erts/configure.in
+++ b/erts/configure.in
@@ -309,6 +309,15 @@ case "$with_microstate_accounting" in
     *) ;;
 esac
 
+AC_ARG_ENABLE(ensure-os-monotonic-time,
+AS_HELP_STRING([--enable-ensure-os-monotonic-time],
+               [enable functionality ensuring the monotonicity of monotonic timestamps delivered by the OS. When a non-monotonic timestamp is detected, it will be replaced by the last delivered monotonic timestamp before being used by Erlang's time functionality. Note that you do *not* want to enable this unless the OS monotonic time source on the system fails to produce monotonic timestamps. This since ensuring the monotonicity of OS monotonic timestamps will hurt scalability and performance of the system.]))
+
+AS_IF([test "$enable_ensure_os_monotonic_time" = "yes"], [
+   AC_DEFINE(ERTS_ENSURE_OS_MONOTONIC_TIME, [1],
+     [Define if ensurance of the monotonicity of OS monotonic timestamps should be enabled])
+  ])
+
 dnl Magic test for clearcase.
 OTP_RELEASE=
 if test "${ERLANG_COMMERCIAL_BUILD}" != ""; then
diff --git a/erts/emulator/beam/erl_lock_check.c b/erts/emulator/beam/erl_lock_check.c
index db1e6e0e66..c16748d7c5 100644
--- a/erts/emulator/beam/erl_lock_check.c
+++ b/erts/emulator/beam/erl_lock_check.c
@@ -163,7 +163,10 @@ static erts_lc_lock_order_t erts_lock_order[] = {
     {	"os_monotonic_time",			NULL			},
     {	"erts_alloc_hard_debug",		NULL			},
     {	"hard_dbg_mseg",		        NULL	                },
-    {	"erts_mmap",				NULL			}
+    {	"erts_mmap",				NULL			},
+#ifdef ERTS_ENSURE_OS_MONOTONIC_TIME
+    {   "ensure_os_monotonic_time",             NULL                    }
+#endif
 };
 
 #define ERTS_LOCK_ORDER_SIZE \
diff --git a/erts/emulator/beam/erl_time_sup.c b/erts/emulator/beam/erl_time_sup.c
index 5c5ab22172..2faf663157 100644
--- a/erts/emulator/beam/erl_time_sup.c
+++ b/erts/emulator/beam/erl_time_sup.c
@@ -36,7 +36,7 @@
 #include "erl_driver.h"
 #include "erl_nif.h"
 #include "erl_proc_sig_queue.h"
- 
+
 static erts_mtx_t erts_get_time_mtx;
 
  /* used by erts_runtime_elapsed_both */
@@ -173,6 +173,10 @@ struct time_sup_infrequently_changed__ {
 
 struct time_sup_frequently_changed__ {
     ErtsMonotonicTime last_not_corrected_time;
+#ifdef ERTS_ENSURE_OS_MONOTONIC_TIME
+    erts_mtx_t mtime_mtx;
+    ErtsMonotonicTime last_delivered_mtime;
+#endif
 };
 
 static struct {
@@ -210,6 +214,49 @@ get_time_offset(void)
     return (ErtsMonotonicTime) erts_atomic64_read_acqb(&time_sup.inf.c.offset);
 }
 
+#if !defined(ERTS_ENSURE_OS_MONOTONIC_TIME)
+
+static ERTS_INLINE ErtsMonotonicTime
+os_monotonic_time(void)
+{
+    return erts_os_monotonic_time();
+}
+
+static ERTS_INLINE void
+os_times(ErtsMonotonicTime *mtimep, ErtsSystemTime *stimep)
+{
+    erts_os_times(mtimep, stimep);
+}
+
+#else /* defined(ERTS_ENSURE_OS_MONOTONIC_TIME) */
+
+static ERTS_INLINE ErtsMonotonicTime
+verify_os_monotonic_time(ErtsMonotonicTime mtime)
+{
+    erts_mtx_lock(&time_sup.f.c.mtime_mtx);
+    if (mtime < time_sup.f.c.last_delivered_mtime)
+        mtime = time_sup.f.c.last_delivered_mtime;
+    else
+        time_sup.f.c.last_delivered_mtime = mtime;
+    erts_mtx_unlock(&time_sup.f.c.mtime_mtx);
+    return mtime;
+}
+
+static ERTS_INLINE ErtsMonotonicTime
+os_monotonic_time(void)
+{
+    return verify_os_monotonic_time(erts_os_monotonic_time());
+}
+
+static ERTS_INLINE void
+os_times(ErtsMonotonicTime *mtimep, ErtsSystemTime *stimep)
+{
+    erts_os_times(mtimep, stimep);
+    *mtimep = verify_os_monotonic_time(*mtimep);
+}
+
+#endif
+
 static ERTS_INLINE void
 update_last_mtime(ErtsSchedulerData *esdp, ErtsMonotonicTime mtime)
 {
@@ -250,7 +297,6 @@ check_os_monotonic_time(ErtsSchedulerData *esdp, ErtsMonotonicTime mtime)
 #endif
 }
 
-
 #ifdef ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT
 
 /*
@@ -319,7 +365,7 @@ read_corrected_time(int os_drift_corrected, ErtsSchedulerData *esdp)
 
     erts_rwmtx_rlock(&time_sup.inf.c.parmon.rwmtx);
 
-    os_mtime = erts_os_monotonic_time();
+    os_mtime = os_monotonic_time();
 
     if (os_mtime >= time_sup.inf.c.parmon.cdata.insts.curr.os_mtime)
 	ci = time_sup.inf.c.parmon.cdata.insts.curr;
@@ -415,7 +461,7 @@ check_time_correction(void *vesdp)
 
     erts_rwmtx_rlock(&time_sup.inf.c.parmon.rwmtx);
 
-    erts_os_times(&os_mtime, &os_stime);
+    os_times(&os_mtime, &os_stime);
 
     ci = time_sup.inf.c.parmon.cdata.insts.curr;
 
@@ -684,9 +730,11 @@ check_time_correction(void *vesdp)
 #endif
 
     if (set_new_correction) {
+        ErtsMonotonicTime cstart_os_mtime;
+
 	erts_rwmtx_rwlock(&time_sup.inf.c.parmon.rwmtx);
 
-	os_mtime = erts_os_monotonic_time();
+	os_mtime = os_monotonic_time();
 
 	/* Save previous correction instance */
 	time_sup.inf.c.parmon.cdata.insts.prev = ci;
@@ -695,21 +743,21 @@ check_time_correction(void *vesdp)
 	 * Current correction instance begin when
 	 * OS monotonic time has increased two units.
 	 */
-	os_mtime += 2;
+	cstart_os_mtime = os_mtime + 2;
 
 	/*
 	 * Erlang monotonic time corresponding to
 	 * next OS monotonic time using previous
 	 * correction.
 	 */
-	erl_mtime = calc_corrected_erl_mtime(os_mtime, &ci, NULL,
+	erl_mtime = calc_corrected_erl_mtime(cstart_os_mtime, &ci, NULL,
 					     os_drift_corrected);
 
 	/*
 	 * Save new current correction instance.
 	 */
 	time_sup.inf.c.parmon.cdata.insts.curr.erl_mtime = erl_mtime;
-	time_sup.inf.c.parmon.cdata.insts.curr.os_mtime = os_mtime;
+	time_sup.inf.c.parmon.cdata.insts.curr.os_mtime = cstart_os_mtime;
 	time_sup.inf.c.parmon.cdata.insts.curr.correction = new_correction;
 
 	erts_rwmtx_rwunlock(&time_sup.inf.c.parmon.rwmtx);
@@ -728,7 +776,7 @@ static ErtsMonotonicTime get_os_corrected_time(ErtsSchedulerData *esdp)
 {
     ErtsMonotonicTime os_mtime;
     ASSERT(time_sup.r.o.warp_mode == ERTS_MULTI_TIME_WARP_MODE);
-    os_mtime = erts_os_monotonic_time();
+    os_mtime = os_monotonic_time();
     check_os_monotonic_time(esdp, os_mtime);
     return os_mtime + time_sup.r.o.moffset;
 }
@@ -742,7 +790,7 @@ check_time_offset(void *vesdp)
 
     ASSERT(time_sup.r.o.warp_mode == ERTS_MULTI_TIME_WARP_MODE);
 
-    erts_os_times(&os_mtime, &os_stime);
+    os_times(&os_mtime, &os_stime);
 
     check_os_monotonic_time(esdp, os_mtime);
 
@@ -792,7 +840,7 @@ init_check_time_correction(void *vesdp)
     old_mtime = ddp->intervals[0].time.mon;
     old_stime = ddp->intervals[0].time.sys;
 
-    erts_os_times(&mtime, &stime);
+    os_times(&mtime, &stime);
 
     check_os_monotonic_time((!vesdp
                              ? erts_get_scheduler_data()
@@ -841,7 +889,7 @@ finalize_corrected_time_offset(ErtsSystemTime *stimep)
 
     erts_rwmtx_rlock(&time_sup.inf.c.parmon.rwmtx);
 
-    erts_os_times(&os_mtime, stimep);
+    os_times(&os_mtime, stimep);
 
     ci = time_sup.inf.c.parmon.cdata.insts.curr;
 
@@ -1015,6 +1063,17 @@ erts_init_time_sup(int time_correction, ErtsTimeWarpMode time_warp_mode)
     runtime_prev.data.user = 0;
     runtime_prev.data.sys = 0;
 
+#ifdef ERTS_ENSURE_OS_MONOTONIC_TIME
+    if (!time_sup.r.o.os_monotonic_time_disable) {
+        time_sup.r.o.os_corrected_monotonic_time = 0; /* we don't trust it... */
+        time_sup.r.o.os_monotonic_time_locked = !0;
+        erts_mtx_init(&time_sup.f.c.mtime_mtx, "ensure_os_monotonic_time",
+                      NIL, (ERTS_LOCK_FLAGS_PROPERTY_STATIC
+                            | ERTS_LOCK_FLAGS_CATEGORY_GENERIC));
+        time_sup.f.c.last_delivered_mtime = erts_os_monotonic_time();
+    }
+#endif
+
     time_sup.r.o.correction = time_correction;
     time_sup.r.o.warp_mode = time_warp_mode;
  
@@ -1167,7 +1226,7 @@ erts_init_time_sup(int time_correction, ErtsTimeWarpMode time_warp_mode)
 	ErtsMonotonicCorrectionData *cdatap;
 	erts_rwmtx_opt_t rwmtx_opts = ERTS_RWMTX_OPT_DEFAULT_INITER;
 	ErtsMonotonicTime offset;
-        erts_os_times(&time_sup.inf.c.minit, &time_sup.inf.c.sinit);
+        os_times(&time_sup.inf.c.minit, &time_sup.inf.c.sinit);
 	time_sup.r.o.moffset = -1*time_sup.inf.c.minit;
 	time_sup.r.o.moffset += ERTS_MONOTONIC_BEGIN;
 	offset = time_sup.inf.c.sinit;
@@ -2136,7 +2195,7 @@ erts_monotonic_time_source(struct process *c_p)
     Sint64 os_mtime = 0;
 #ifdef ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT
     if (!time_sup.r.o.os_monotonic_time_disable)
-	os_mtime = (Sint64) erts_os_monotonic_time();
+	os_mtime = (Sint64) os_monotonic_time();
 #endif
 
     bld_monotonic_time_source(NULL, &hsz, os_mtime);
diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h
index 1ffc7f5028..f9731f5396 100644
--- a/erts/emulator/beam/sys.h
+++ b/erts/emulator/beam/sys.h
@@ -720,6 +720,10 @@ extern char *erts_default_arg0;
 
 extern char os_type[];
 
+#if !defined(ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT)
+#  undef ERTS_ENSURE_OS_MONOTONIC_TIME
+#endif
+
 typedef struct {
     int have_os_monotonic_time;
     int have_corrected_os_monotonic_time;
diff --git a/make/configure b/make/configure
index c44049816c..c241dafa4e 100755
--- a/make/configure
+++ b/make/configure
@@ -767,6 +767,7 @@ enable_m64_build
 enable_m32_build
 enable_pie
 with_libatomic_ops
+enable_ensure_os_monotonic_time
 enable_sanitizers
 enable_silent_rules
 '
@@ -1473,6 +1474,17 @@ Optional Features:
   --enable-m32-build      build 32bit binaries using the -m32 flag to (g)cc
   --enable-pie            build position independent executables
   --disable-pie           do no build position independent executables
+  --enable-ensure-os-monotonic-time
+                          enable functionality ensuring the monotonicity of
+                          monotonic timestamps delivered by the OS. When a
+                          non-monotonic timestamp is detected, it will be
+                          replaced by the last delivered monotonic timestamp
+                          before being used by Erlang's time functionality.
+                          Note that you do *not* want to enable this unless
+                          the OS monotonic time source on the system fails to
+                          produce monotonic timestamps. This since ensuring
+                          the monotonicity of OS monotonic timestamps will
+                          hurt scalability and performance of the system.
   --enable-sanitizers[=comma-separated list of sanitizers]
                           Default=address,undefined
   --enable-silent-rules   less verbose build output (undo: "make V=1")
@@ -4650,6 +4662,12 @@ if test "${with_libatomic_ops+set}" = set; then :
 fi
 
 
+# Check whether --enable-ensure-os-monotonic-time was given.
+if test "${enable_ensure_os_monotonic_time+set}" = set; then :
+  enableval=$enable_ensure_os_monotonic_time;
+fi
+
+
 
 # Check whether --enable-sanitizers was given.
 if test "${enable_sanitizers+set}" = set; then :
diff --git a/make/configure.in b/make/configure.in
index 660c51e363..83a24f12b6 100644
--- a/make/configure.in
+++ b/make/configure.in
@@ -368,6 +368,10 @@ AC_ARG_WITH(libatomic_ops,
 	    AS_HELP_STRING([--with-libatomic_ops=PATH],
 			   [specify and prefer usage of libatomic_ops in the ethread library]))
 
+AC_ARG_ENABLE(ensure-os-monotonic-time,
+AS_HELP_STRING([--enable-ensure-os-monotonic-time],
+               [enable functionality ensuring the monotonicity of monotonic timestamps delivered by the OS. When a non-monotonic timestamp is detected, it will be replaced by the last delivered monotonic timestamp before being used by Erlang's time functionality. Note that you do *not* want to enable this unless the OS monotonic time source on the system fails to produce monotonic timestamps. This since ensuring the monotonicity of OS monotonic timestamps will hurt scalability and performance of the system.]))
+
 m4_define(DEFAULT_SANITIZERS, [address,undefined])
 AC_ARG_ENABLE(sanitizers,
     AS_HELP_STRING(
-- 
2.35.3

openSUSE Build Service is sponsored by