]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - timing.c
Patch from Robert de Bath to substantially simplify timing.c.
[PuTTY.git] / timing.c
1 /*
2  * timing.c
3  * 
4  * This module tracks any timers set up by schedule_timer(). It
5  * keeps all the currently active timers in a list; it informs the
6  * front end of when the next timer is due to go off if that
7  * changes; and, very importantly, it tracks the context pointers
8  * passed to schedule_timer(), so that if a context is freed all
9  * the timers associated with it can be immediately annulled.
10  *
11  *
12  * The problem is that computer clocks aren't perfectly accurate.
13  * The GETTICKCOUNT function returns a 32bit number that normally
14  * increases by about 1000 every second. On windows this uses the PC's
15  * interrupt timer and so is only accurate to around 20ppm.  On unix it's
16  * a value that's calculated from the current UTC time and so is in theory
17  * accurate in the long term but may jitter and jump in the short term.
18  *
19  * What PuTTY needs from these timers is simply a way of delaying the
20  * calling of a function for a little while, if it's occasionally called a
21  * little early or late that's not a problem. So to protect against clock
22  * jumps schedule_timer records the time that it was called in the timer
23  * structure. With this information the run_timers function can see when
24  * the current GETTICKCOUNT value is after the time the event should be
25  * fired OR before the time it was set. In the latter case the clock must
26  * have jumped, the former is (probably) just the normal passage of time.
27  *
28  */
29
30 #include <assert.h>
31 #include <stdio.h>
32
33 #include "putty.h"
34 #include "tree234.h"
35
36 struct timer {
37     timer_fn_t fn;
38     void *ctx;
39     long now;
40     long when_set;
41 };
42
43 static tree234 *timers = NULL;
44 static tree234 *timer_contexts = NULL;
45 static long now = 0L;
46
47 static int compare_timers(void *av, void *bv)
48 {
49     struct timer *a = (struct timer *)av;
50     struct timer *b = (struct timer *)bv;
51     long at = a->now - now;
52     long bt = b->now - now;
53
54     if (at < bt)
55         return -1;
56     else if (at > bt)
57         return +1;
58
59     /*
60      * Failing that, compare on the other two fields, just so that
61      * we don't get unwanted equality.
62      */
63 #ifdef __LCC__
64     /* lcc won't let us compare function pointers. Legal, but annoying. */
65     {
66         int c = memcmp(&a->fn, &b->fn, sizeof(a->fn));
67         if (c < 0)
68             return -1;
69         else if (c > 0)
70             return +1;
71     }
72 #else    
73     if (a->fn < b->fn)
74         return -1;
75     else if (a->fn > b->fn)
76         return +1;
77 #endif
78
79     if (a->ctx < b->ctx)
80         return -1;
81     else if (a->ctx > b->ctx)
82         return +1;
83
84     /*
85      * Failing _that_, the two entries genuinely are equal, and we
86      * never have a need to store them separately in the tree.
87      */
88     return 0;
89 }
90
91 static int compare_timer_contexts(void *av, void *bv)
92 {
93     char *a = (char *)av;
94     char *b = (char *)bv;
95     if (a < b)
96         return -1;
97     else if (a > b)
98         return +1;
99     return 0;
100 }
101
102 static void init_timers(void)
103 {
104     if (!timers) {
105         timers = newtree234(compare_timers);
106         timer_contexts = newtree234(compare_timer_contexts);
107         now = GETTICKCOUNT();
108     }
109 }
110
111 long schedule_timer(int ticks, timer_fn_t fn, void *ctx)
112 {
113     long when;
114     struct timer *t, *first;
115
116     init_timers();
117
118     now = GETTICKCOUNT();
119     when = ticks + now;
120
121     /*
122      * Just in case our various defences against timing skew fail
123      * us: if we try to schedule a timer that's already in the
124      * past, we instead schedule it for the immediate future.
125      */
126     if (when - now <= 0)
127         when = now + 1;
128
129     t = snew(struct timer);
130     t->fn = fn;
131     t->ctx = ctx;
132     t->now = when;
133     t->when_set = now;
134
135     if (t != add234(timers, t)) {
136         sfree(t);                      /* identical timer already exists */
137     } else {
138         add234(timer_contexts, t->ctx);/* don't care if this fails */
139     }
140
141     first = (struct timer *)index234(timers, 0);
142     if (first == t) {
143         /*
144          * This timer is the very first on the list, so we must
145          * notify the front end.
146          */
147         timer_change_notify(first->now);
148     }
149
150     return when;
151 }
152
153 /*
154  * Call to run any timers whose time has reached the present.
155  * Returns the time (in ticks) expected until the next timer after
156  * that triggers.
157  */
158 int run_timers(long anow, long *next)
159 {
160     struct timer *first;
161
162     init_timers();
163
164     now = GETTICKCOUNT();
165
166     while (1) {
167         first = (struct timer *)index234(timers, 0);
168
169         if (!first)
170             return FALSE;              /* no timers remaining */
171
172         if (find234(timer_contexts, first->ctx, NULL) == NULL) {
173             /*
174              * This timer belongs to a context that has been
175              * expired. Delete it without running.
176              */
177             delpos234(timers, 0);
178             sfree(first);
179         } else if (first->now - now <= 0 ||
180                    now - (first->when_set - 10) < 0) {
181             /*
182              * This timer is active and has reached its running
183              * time. Run it.
184              */
185             delpos234(timers, 0);
186             first->fn(first->ctx, first->now);
187             sfree(first);
188         } else {
189             /*
190              * This is the first still-active timer that is in the
191              * future. Return how long it has yet to go.
192              */
193             *next = first->now;
194             return TRUE;
195         }
196     }
197 }
198
199 /*
200  * Call to expire all timers associated with a given context.
201  */
202 void expire_timer_context(void *ctx)
203 {
204     init_timers();
205
206     /*
207      * We don't bother to check the return value; if the context
208      * already wasn't in the tree (presumably because no timers
209      * ever actually got scheduled for it) then that's fine and we
210      * simply don't need to do anything.
211      */
212     del234(timer_contexts, ctx);
213 }