X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/39934deb5202149f98198c111a35c21cb4d0d0f8..HEAD:/timing.c diff --git a/timing.c b/timing.c index a87be897..ccd76cd6 100644 --- 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 @@ -18,12 +36,13 @@ struct timer { timer_fn_t fn; void *ctx; - long now; + unsigned long now; + unsigned long when_set; }; static tree234 *timers = NULL; static tree234 *timer_contexts = NULL; -static long now = 0L; +static unsigned long now = 0L; static int compare_timers(void *av, void *bv) { @@ -41,10 +60,19 @@ static int compare_timers(void *av, void *bv) * Failing that, compare on the other two fields, just so that * we don't get unwanted equality. */ +#if defined(__LCC__) || defined(__clang__) + /* lcc won't let us compare function pointers. Legal, but annoying. */ + { + int c = memcmp(&a->fn, &b->fn, sizeof(a->fn)); + if (c) + return c; + } +#else if (a->fn < b->fn) return -1; else if (a->fn > b->fn) return +1; +#endif if (a->ctx < b->ctx) return -1; @@ -78,20 +106,29 @@ static void init_timers(void) } } -long schedule_timer(int ticks, timer_fn_t fn, void *ctx) +unsigned long schedule_timer(int ticks, timer_fn_t fn, void *ctx) { - long when; + unsigned long when; struct timer *t, *first; init_timers(); - when = ticks + GETTICKCOUNT(); - assert(when - now > 0); + now = GETTICKCOUNT(); + when = ticks + now; + + /* + * Just in case our various defences against timing skew fail + * us: if we try to schedule a timer that's already in the + * past, we instead schedule it for the immediate future. + */ + if (when - now <= 0) + when = now + 1; t = snew(struct timer); t->fn = fn; t->ctx = ctx; t->now = when; + t->when_set = now; if (t != add234(timers, t)) { sfree(t); /* identical timer already exists */ @@ -116,13 +153,13 @@ long schedule_timer(int ticks, timer_fn_t fn, void *ctx) * Returns the time (in ticks) expected until the next timer after * that triggers. */ -int run_timers(long anow, long *next) +int run_timers(unsigned long anow, unsigned long *next) { struct timer *first; init_timers(); - now = anow; + now = GETTICKCOUNT(); while (1) { first = (struct timer *)index234(timers, 0); @@ -137,7 +174,8 @@ int run_timers(long anow, long *next) */ delpos234(timers, 0); sfree(first); - } else if (first->now - now <= 0) { + } else if (now - (first->when_set - 10) > + first->now - (first->when_set - 10)) { /* * This timer is active and has reached its running * time. Run it.