gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[libmicrohttpd] 05/05: Added workaround for system clock jumps back


From: gnunet
Subject: [libmicrohttpd] 05/05: Added workaround for system clock jumps back
Date: Sun, 26 Dec 2021 10:43:06 +0100

This is an automated email from the git hooks/post-receive script.

karlson2k pushed a commit to branch master
in repository libmicrohttpd.

commit 73c43a7ef7bdccda1b6b6714d2d64b1714e9cdad
Author: Evgeny Grin (Karlson2k) <k2k@narod.ru>
AuthorDate: Sun Dec 26 12:02:04 2021 +0300

    Added workaround for system clock jumps back
    
    Same OSes (namely OpenBSD) has system clocks that must be monotonic,
    but in practice clocks may jump forward and back up to 500 milliseconds.
---
 src/microhttpd/connection.c |  69 +++++++++++++++++++++------
 src/microhttpd/daemon.c     | 113 ++++++++++++++++++++++++--------------------
 2 files changed, 116 insertions(+), 66 deletions(-)

diff --git a/src/microhttpd/connection.c b/src/microhttpd/connection.c
index 02fc0e71..84bfc662 100644
--- a/src/microhttpd/connection.c
+++ b/src/microhttpd/connection.c
@@ -4180,6 +4180,56 @@ MHD_connection_handle_write (struct MHD_Connection 
*connection)
 }
 
 
+/**
+ * Check whether connection has timed out.
+ * @param c the connection to check
+ * @return true if connection has timeout and needs to be closed,
+ *         false otherwise.
+ */
+static bool
+connection_check_timedout (struct MHD_Connection *c)
+{
+  const uint64_t timeout = c->connection_timeout_ms;
+  uint64_t now;
+  uint64_t since_actv;
+
+  if (c->suspended)
+    return false;
+  if (0 == timeout)
+    return false;
+  now = MHD_monotonic_msec_counter ();
+  since_actv = now - c->last_activity;
+  /* Keep the next lines in sync with #connection_get_wait() to avoid
+   * undesired side-effects like busy-waiting. */
+  if (timeout < since_actv)
+  {
+    if (UINT64_MAX / 2 < since_actv)
+    {
+      const uint64_t jump_back = c->last_activity - now;
+      /* Very unlikely that it is more than quarter-million years pause.
+       * More likely that system clock jumps back. */
+      if (5000 >= jump_back)
+      {
+#ifdef HAVE_MESSAGES
+        MHD_DLOG (c->daemon,
+                  _ ("Detected system clock %u milliseconds jump back.\n"),
+                  (unsigned int) jump_back);
+#endif
+        return false;
+      }
+#ifdef HAVE_MESSAGES
+      MHD_DLOG (c->daemon,
+                _ ("Detected too large system clock %" PRIu64 " milliseconds "
+                   "jump back.\n"),
+                jump_back);
+#endif
+    }
+    return true;
+  }
+  return false;
+}
+
+
 /**
  * Clean up the state of the given connection and move it into the
  * clean up queue for final disposal.
@@ -4799,21 +4849,12 @@ MHD_connection_handle_idle (struct MHD_Connection 
*connection)
     }
     break;
   }
-  if (! connection->suspended)
+  if (connection_check_timedout (connection))
   {
-    uint64_t timeout;
-    timeout = connection->connection_timeout_ms;
-    /* Keep the next lines in sync with #MHD_get_timeout() to avoid
-     * undesired side-effects like busy-waiting. */
-    if ( (0 != timeout) &&
-         (timeout < (MHD_monotonic_msec_counter ()
-                     - connection->last_activity)) )
-    {
-      MHD_connection_close_ (connection,
-                             MHD_REQUEST_TERMINATED_TIMEOUT_REACHED);
-      connection->in_idle = false;
-      return MHD_YES;
-    }
+    MHD_connection_close_ (connection,
+                           MHD_REQUEST_TERMINATED_TIMEOUT_REACHED);
+    connection->in_idle = false;
+    return MHD_YES;
   }
   MHD_connection_update_event_loop_info (connection);
   ret = MHD_YES;
diff --git a/src/microhttpd/daemon.c b/src/microhttpd/daemon.c
index d4064dd7..5ca612ea 100644
--- a/src/microhttpd/daemon.c
+++ b/src/microhttpd/daemon.c
@@ -1857,6 +1857,54 @@ thread_main_connection_upgrade (struct MHD_Connection 
*con)
 #endif /* UPGRADE_SUPPORT */
 
 
+/**
+ * Get maximum wait period for the connection (the amount of time left before
+ * connection time out)
+ * @param c the connection to check
+ * @return the maximum wait period before the connection must be processed
+ *         again.
+ */
+static uint64_t
+connection_get_wait (struct MHD_Connection *c)
+{
+  const uint64_t now = MHD_monotonic_msec_counter ();
+  const uint64_t since_actv = now - c->last_activity;
+  const uint64_t timeout = c->connection_timeout_ms;
+  uint64_t mseconds_left;
+
+  mhd_assert (0 != timeout);
+  /* Keep the next lines in sync with #connection_check_timedout() to avoid
+   * undesired side-effects like busy-waiting. */
+  if (timeout < since_actv)
+  {
+    if (UINT64_MAX / 2 < since_actv)
+    {
+      const uint64_t jump_back = c->last_activity - now;
+      /* Very unlikely that it is more than quarter-million years pause.
+       * More likely that system clock jumps back. */
+      if (5000 >= jump_back)
+      { /* Jump back is less than 5 seconds, try to recover. */
+        return 100; /* Set wait time to 0.1 seconds */
+      }
+      /* Too large jump back */
+    }
+    return 0; /* Connection has timed out */
+  }
+  else if (since_actv == timeout)
+  {
+    /* Exact match for timeout and time from last activity.
+     * Maybe this is just a precise match or this happens because the timer
+     * resolution is too low.
+     * Set wait time to 0.1 seconds to avoid busy-waiting with low
+     * timer resolution as connection is not timed-out yet. */
+    return 100;
+  }
+  mseconds_left = timeout - since_actv;
+
+  return mseconds_left;
+}
+
+
 /**
  * Main function of the thread that handles an individual
  * connection when #MHD_USE_THREAD_PER_CONNECTION is set.
@@ -1995,35 +2043,16 @@ thread_main_handle_connection (void *data)
     if ( (NULL == tvp) &&
          (timeout > 0) )
     {
-      const uint64_t since_actv = MHD_monotonic_msec_counter ()
-                                  - con->last_activity;
-      if (since_actv > timeout)
-      {
-        tv.tv_sec = 0;
-        tv.tv_usec = 0;
-      }
-      else if (since_actv == timeout)
-      {
-        /* Exact match for timeout and time from last activity.
-         * Maybe this is just a precise match or this happens because the timer
-         * resolution is too low.
-         * Set wait time to 0.1 seconds to avoid busy-waiting with low
-         * timer resolution as connection is not yet timed-out */
-        tv.tv_sec = 0;
-        tv.tv_usec = 100 * 1000;
-      }
+      const uint64_t mseconds_left = connection_get_wait (con);
+#if (SIZEOF_UINT64_T - 2) >= SIZEOF_STRUCT_TIMEVAL_TV_SEC
+      if (mseconds_left / 1000 > TIMEVAL_TV_SEC_MAX)
+        tv.tv_sec = TIMEVAL_TV_SEC_MAX;
       else
-      {
-        const uint64_t mseconds_left = timeout - since_actv;
-#if (SIZEOF_UINT64_T - 1) >= SIZEOF_STRUCT_TIMEVAL_TV_SEC
-        if (mseconds_left / 1000 > TIMEVAL_TV_SEC_MAX)
-          tv.tv_sec = TIMEVAL_TV_SEC_MAX;
-        else
-#endif /* (SIZEOF_UINT64_T - 1) >= SIZEOF_STRUCT_TIMEVAL_TV_SEC */
-        tv.tv_sec = (_MHD_TIMEVAL_TV_SEC_TYPE) mseconds_left / 1000;
+#endif /* (SIZEOF_UINT64_T - 2) >= SIZEOF_STRUCT_TIMEVAL_TV_SEC */
+      tv.tv_sec = (_MHD_TIMEVAL_TV_SEC_TYPE) mseconds_left / 1000;
+
+      tv.tv_usec = (mseconds_left % 1000) * 1000;
 
-        tv.tv_usec = (mseconds_left % 1000) * 1000;
-      }
       tvp = &tv;
     }
     if (! use_poll)
@@ -3930,33 +3959,13 @@ MHD_get_timeout (struct MHD_Daemon *daemon,
 
   if (NULL != earliest_tmot_conn)
   {
-    const uint64_t since_actv = MHD_monotonic_msec_counter ()
-                                - earliest_tmot_conn->last_activity;
-    /* Keep the next lines in sync with #MHD_connection_handle_idle() and
-     * with #thread_main_handle_connection(). */
-    if (since_actv > earliest_tmot_conn->connection_timeout_ms)
-      *timeout = 0;
-    else if (since_actv == earliest_tmot_conn->connection_timeout_ms)
-    {
-      /* Exact match for timeout and time from last activity.
-       * Maybe this is just a precise match or this happens because the timer
-       * resolution is too low.
-       * Set wait time to 0.1 seconds to avoid busy-waiting with low
-       * timer resolution as connection is not yet timed-out */
-      *timeout = 100;
-    }
-    else
-    {
-      const uint64_t mssecond_left = earliest_tmot_conn->connection_timeout_ms
-                                     - since_actv;
-
+    const uint64_t mssecond_left = connection_get_wait (earliest_tmot_conn);
 #if SIZEOF_UINT64_T > SIZEOF_UNSIGNED_LONG_LONG
-      if (mssecond_left > ULLONG_MAX)
-        *timeout = ULLONG_MAX;
-      else
+    if (mssecond_left > ULLONG_MAX)
+      *timeout = ULLONG_MAX;
+    else
 #endif /* UINT64 != ULLONG_MAX */
-      *timeout = (unsigned long long) mssecond_left;
-    }
+    *timeout = (unsigned long long) mssecond_left;
     return MHD_YES;
   }
   return MHD_NO;

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]