* 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>
timer_fn_t fn;
void *ctx;
long now;
+ long when_set;
};
static tree234 *timers = NULL;
* 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;
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 */
init_timers();
- now = anow;
+ now = GETTICKCOUNT();
while (1) {
first = (struct timer *)index234(timers, 0);
*/
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.