Patch from Robert de Bath to substantially simplify timing.c.
authorsimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Sun, 13 May 2012 15:59:26 +0000 (15:59 +0000)
committersimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Sun, 13 May 2012 15:59:26 +0000 (15:59 +0000)
The previous platform-dependent ifdefs, switching between a system
which tried to cope with spurious callbacks (which I'd observed on
Windows) and one which tried to cope with system clock jumps (which
can happen on Unix, if you use gettimeofday) have been completely
removed, and replaced with a much simpler approach which just copes
with system clock jumps by triggering any timers immediately.

None of the resulting effects should be catastrophic (the worst thing
might be the waste of CPU in a spurious rekey, but as long as the
system clock isn't jumping around _all_ the time that's hardly
critical) and in any case the Unix port has had a long-standing oddity
involving occasional lockups if pterm or PuTTY runs for too long,
which hopefully this should replace with a much less bad failure mode.
And the code is much simpler, which is not to be sneezed at.

git-svn-id: svn://svn.tartarus.org/sgt/putty@9528 cda61777-01e9-0310-a592-d414129be87e

timing.c
unix/unix.h
unix/uxmisc.c
unix/uxplink.c
unix/uxsftp.c
windows/winstuff.h

index abb9b46..841d973 100644 (file)
--- a/timing.c
+++ b/timing.c
@@ -7,6 +7,24 @@
  * changes; and, very importantly, it tracks the context pointers
  * passed to schedule_timer(), so that if a context is freed all
  * the timers associated with it can be immediately annulled.
+ *
+ *
+ * The problem is that computer clocks aren't perfectly accurate.
+ * The GETTICKCOUNT function returns a 32bit number that normally
+ * increases by about 1000 every second. On windows this uses the PC's
+ * interrupt timer and so is only accurate to around 20ppm.  On unix it's
+ * a value that's calculated from the current UTC time and so is in theory
+ * accurate in the long term but may jitter and jump in the short term.
+ *
+ * What PuTTY needs from these timers is simply a way of delaying the
+ * calling of a function for a little while, if it's occasionally called a
+ * little early or late that's not a problem. So to protect against clock
+ * jumps schedule_timer records the time that it was called in the timer
+ * structure. With this information the run_timers function can see when
+ * the current GETTICKCOUNT value is after the time the event should be
+ * fired OR before the time it was set. In the latter case the clock must
+ * have jumped, the former is (probably) just the normal passage of time.
+ *
  */
 
 #include <assert.h>
@@ -19,6 +37,7 @@ struct timer {
     timer_fn_t fn;
     void *ctx;
     long now;
+    long when_set;
 };
 
 static tree234 *timers = NULL;
@@ -96,7 +115,8 @@ long schedule_timer(int ticks, timer_fn_t fn, void *ctx)
 
     init_timers();
 
-    when = ticks + GETTICKCOUNT();
+    now = GETTICKCOUNT();
+    when = ticks + now;
 
     /*
      * Just in case our various defences against timing skew fail
@@ -110,6 +130,7 @@ long schedule_timer(int ticks, timer_fn_t fn, void *ctx)
     t->fn = fn;
     t->ctx = ctx;
     t->now = when;
+    t->when_set = now;
 
     if (t != add234(timers, t)) {
        sfree(t);                      /* identical timer already exists */
@@ -140,59 +161,7 @@ int run_timers(long anow, long *next)
 
     init_timers();
 
-#ifdef TIMING_SYNC
-    /*
-     * In this ifdef I put some code which deals with the
-     * possibility that `anow' disagrees with GETTICKCOUNT by a
-     * significant margin. Our strategy for dealing with it differs
-     * depending on platform, because on some platforms
-     * GETTICKCOUNT is more likely to be right whereas on others
-     * `anow' is a better gold standard.
-     */
-    {
-       long tnow = GETTICKCOUNT();
-
-       if (tnow + TICKSPERSEC/50 - anow < 0 ||
-           anow + TICKSPERSEC/50 - tnow < 0
-           ) {
-#if defined TIMING_SYNC_ANOW
-           /*
-            * If anow is accurate and the tick count is wrong,
-            * this is likely to be because the tick count is
-            * derived from the system clock which has changed (as
-            * can occur on Unix). Therefore, we resolve this by
-            * inventing an offset which is used to adjust all
-            * future output from GETTICKCOUNT.
-            * 
-            * A platform which defines TIMING_SYNC_ANOW is
-            * expected to have also defined this offset variable
-            * in (its platform-specific adjunct to) putty.h.
-            * Therefore we can simply reference it here and assume
-            * that it will exist.
-            */
-           tickcount_offset += anow - tnow;
-#elif defined TIMING_SYNC_TICKCOUNT
-           /*
-            * If the tick count is more likely to be accurate, we
-            * simply use that as our time value, which may mean we
-            * run no timers in this call (because we got called
-            * early), or alternatively it may mean we run lots of
-            * timers in a hurry because we were called late.
-            */
-           anow = tnow;
-#else
-/*
- * Any platform which defines TIMING_SYNC must also define one of the two
- * auxiliary symbols TIMING_SYNC_ANOW and TIMING_SYNC_TICKCOUNT, to
- * indicate which measurement to trust when the two disagree.
- */
-#error TIMING_SYNC definition incomplete
-#endif
-       }
-    }
-#endif
-
-    now = anow;
+    now = GETTICKCOUNT();
 
     while (1) {
        first = (struct timer *)index234(timers, 0);
@@ -207,7 +176,8 @@ int run_timers(long anow, long *next)
             */
            delpos234(timers, 0);
            sfree(first);
-       } else if (first->now - now <= 0) {
+       } else if (first->now - now <= 0 ||
+                  now - (first->when_set - 10) < 0) {
            /*
             * This timer is active and has reached its running
             * time. Run it.
index 2ffc356..feef0fb 100644 (file)
@@ -60,11 +60,6 @@ unsigned long getticks(void);               /* based on gettimeofday(2) */
 #define GETTICKCOUNT getticks
 #define TICKSPERSEC    1000           /* we choose to use milliseconds */
 #define CURSORBLINK     450           /* no standard way to set this */
-/* getticks() works using gettimeofday(), so it's vulnerable to system clock
- * changes causing chaos. Therefore, we provide a compensation mechanism. */
-#define TIMING_SYNC
-#define TIMING_SYNC_ANOW
-extern long tickcount_offset;
 
 #define WCHAR wchar_t
 #define BYTE unsigned char
index 8441349..d11b2f7 100644 (file)
@@ -13,8 +13,6 @@
 
 #include "putty.h"
 
-long tickcount_offset = 0;
-
 unsigned long getticks(void)
 {
     struct timeval tv;
@@ -24,7 +22,7 @@ unsigned long getticks(void)
      * because we need a decent number of them to fit into a 32-bit
      * word so it can be used for keepalives.
      */
-    return tv.tv_sec * 1000 + tv.tv_usec / 1000 + tickcount_offset;
+    return tv.tv_sec * TICKSPERSEC + tv.tv_usec / (1000000 / TICKSPERSEC);
 }
 
 Filename *filename_from_str(const char *str)
index 85f5352..77c46a4 100644 (file)
@@ -1027,27 +1027,8 @@ int main(int argc, char **argv)
            ret = select(maxfd, &rset, &wset, &xset, ptv);
            if (ret == 0)
                now = next;
-           else {
-               long newnow = GETTICKCOUNT();
-               /*
-                * Check to see whether the system clock has
-                * changed massively during the select.
-                */
-               if (newnow - now < 0 || newnow - now > next - now) {
-                   /*
-                    * If so, look at the elapsed time in the
-                    * select and use it to compute a new
-                    * tickcount_offset.
-                    */
-                   long othernow = now + tv.tv_sec * 1000 + tv.tv_usec / 1000;
-                   /* So we'd like GETTICKCOUNT to have returned othernow,
-                    * but instead it return newnow. Hence ... */
-                   tickcount_offset += othernow - newnow;
-                   now = othernow;
-               } else {
-                   now = newnow;
-               }
-           }
+           else
+               now = GETTICKCOUNT();
        } while (ret < 0 && errno == EINTR);
 
        if (ret < 0) {
index c266fb7..a92cfc9 100644 (file)
@@ -505,27 +505,8 @@ static int ssh_sftp_do_select(int include_stdin, int no_fds_ok)
            ret = select(maxfd, &rset, &wset, &xset, ptv);
            if (ret == 0)
                now = next;
-           else {
-               long newnow = GETTICKCOUNT();
-               /*
-                * Check to see whether the system clock has
-                * changed massively during the select.
-                */
-               if (newnow - now < 0 || newnow - now > next - now) {
-                   /*
-                    * If so, look at the elapsed time in the
-                    * select and use it to compute a new
-                    * tickcount_offset.
-                    */
-                   long othernow = now + tv.tv_sec * 1000 + tv.tv_usec / 1000;
-                   /* So we'd like GETTICKCOUNT to have returned othernow,
-                    * but instead it return newnow. Hence ... */
-                   tickcount_offset += othernow - newnow;
-                   now = othernow;
-               } else {
-                   now = newnow;
-               }
-           }
+           else
+               now = GETTICKCOUNT();
        } while (ret < 0 && errno != EINTR);
     } while (ret == 0);
 
index 3ca0427..df59631 100644 (file)
@@ -237,15 +237,6 @@ GLOBAL void *logctx;
                                 "All Files (*.*)\0*\0\0\0")
 
 /*
- * On some versions of Windows, it has been known for WM_TIMER to
- * occasionally get its callback time simply wrong, and call us
- * back several minutes early. Defining these symbols enables
- * compensation code in timing.c.
- */
-#define TIMING_SYNC
-#define TIMING_SYNC_TICKCOUNT
-
-/*
  * winnet.c dynamically loads WinSock 2 or WinSock 1 depending on
  * what it can get, which means any WinSock routines used outside
  * that module must be exported from it as function pointers. So