* 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>
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)
{
* Failing that, compare on the other two fields, just so that
* we don't get unwanted equality.
*/
-#ifdef __LCC__
+#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 < 0)
- return -1;
- else if (c > 0)
- return +1;
+ if (c)
+ return c;
}
#else
if (a->fn < b->fn)
}
}
-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 */
* 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);
*/
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.