1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-09 09:27:59 +00:00
putty-source/timing.c

223 lines
6.0 KiB
C
Raw Permalink Normal View History

/*
* timing.c
*
* This module tracks any timers set up by schedule_timer(). It
* keeps all the currently active timers in a list; it informs the
* front end of when the next timer is due to go off if that
* 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>
#include <stdio.h>
#include "putty.h"
#include "tree234.h"
struct timer {
timer_fn_t fn;
void *ctx;
unsigned long now;
unsigned long when_set;
};
static tree234 *timers = NULL;
static tree234 *timer_contexts = NULL;
static unsigned long now = 0L;
static int compare_timers(void *av, void *bv)
{
struct timer *a = (struct timer *)av;
struct timer *b = (struct timer *)bv;
long at = a->now - now;
long bt = b->now - now;
if (at < bt)
return -1;
else if (at > bt)
return +1;
/*
* 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;
else if (a->ctx > b->ctx)
return +1;
/*
* Failing _that_, the two entries genuinely are equal, and we
* never have a need to store them separately in the tree.
*/
return 0;
}
static int compare_timer_contexts(void *av, void *bv)
{
char *a = (char *)av;
char *b = (char *)bv;
if (a < b)
return -1;
else if (a > b)
return +1;
return 0;
}
static void init_timers(void)
{
if (!timers) {
timers = newtree234(compare_timers);
timer_contexts = newtree234(compare_timer_contexts);
now = GETTICKCOUNT();
}
}
unsigned long schedule_timer(int ticks, timer_fn_t fn, void *ctx)
{
unsigned long when;
struct timer *t, *first;
init_timers();
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 */
} else {
add234(timer_contexts, t->ctx);/* don't care if this fails */
}
first = (struct timer *)index234(timers, 0);
if (first == t) {
/*
* This timer is the very first on the list, so we must
* notify the front end.
*/
timer_change_notify(first->now);
}
return when;
}
unsigned long timing_last_clock(void)
{
/*
* Return the last value we stored in 'now'. In particular,
* calling this just after schedule_timer returns the value of
* 'now' that was used to decide when the timer you just set would
* go off.
*/
return now;
}
/*
* Call to run any timers whose time has reached the present.
* Returns the time (in ticks) expected until the next timer after
* that triggers.
*/
Convert a lot of 'int' variables to 'bool'. My normal habit these days, in new code, is to treat int and bool as _almost_ completely separate types. I'm still willing to use C's implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine, no need to spell it out as blob.len != 0), but generally, if a variable is going to be conceptually a boolean, I like to declare it bool and assign to it using 'true' or 'false' rather than 0 or 1. PuTTY is an exception, because it predates the C99 bool, and I've stuck to its existing coding style even when adding new code to it. But it's been annoying me more and more, so now that I've decided C99 bool is an acceptable thing to require from our toolchain in the first place, here's a quite thorough trawl through the source doing 'boolification'. Many variables and function parameters are now typed as bool rather than int; many assignments of 0 or 1 to those variables are now spelled 'true' or 'false'. I managed this thorough conversion with the help of a custom clang plugin that I wrote to trawl the AST and apply heuristics to point out where things might want changing. So I've even managed to do a decent job on parts of the code I haven't looked at in years! To make the plugin's work easier, I pushed platform front ends generally in the direction of using standard 'bool' in preference to platform-specific boolean types like Windows BOOL or GTK's gboolean; I've left the platform booleans in places they _have_ to be for the platform APIs to work right, but variables only used by my own code have been converted wherever I found them. In a few places there are int values that look very like booleans in _most_ of the places they're used, but have a rarely-used third value, or a distinction between different nonzero values that most users don't care about. In these cases, I've _removed_ uses of 'true' and 'false' for the return values, to emphasise that there's something more subtle going on than a simple boolean answer: - the 'multisel' field in dialog.h's list box structure, for which the GTK front end in particular recognises a difference between 1 and 2 but nearly everything else treats as boolean - the 'urgent' parameter to plug_receive, where 1 vs 2 tells you something about the specific location of the urgent pointer, but most clients only care about 0 vs 'something nonzero' - the return value of wc_match, where -1 indicates a syntax error in the wildcard. - the return values from SSH-1 RSA-key loading functions, which use -1 for 'wrong passphrase' and 0 for all other failures (so any caller which already knows it's not loading an _encrypted private_ key can treat them as boolean) - term->esc_query, and the 'query' parameter in toggle_mode in terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h, but can also hold -1 for some other intervening character that we don't support. In a few places there's an integer that I haven't turned into a bool even though it really _can_ only take values 0 or 1 (and, as above, tried to make the call sites consistent in not calling those values true and false), on the grounds that I thought it would make it more confusing to imply that the 0 value was in some sense 'negative' or bad and the 1 positive or good: - the return value of plug_accepting uses the POSIXish convention of 0=success and nonzero=error; I think if I made it bool then I'd also want to reverse its sense, and that's a job for a separate piece of work. - the 'screen' parameter to lineptr() in terminal.c, where 0 and 1 represent the default and alternate screens. There's no obvious reason why one of those should be considered 'true' or 'positive' or 'success' - they're just indices - so I've left it as int. ssh_scp_recv had particularly confusing semantics for its previous int return value: its call sites used '<= 0' to check for error, but it never actually returned a negative number, just 0 or 1. Now the function and its call sites agree that it's a bool. In a couple of places I've renamed variables called 'ret', because I don't like that name any more - it's unclear whether it means the return value (in preparation) for the _containing_ function or the return value received from a subroutine call, and occasionally I've accidentally used the same variable for both and introduced a bug. So where one of those got in my way, I've renamed it to 'toret' or 'retd' (the latter short for 'returned') in line with my usual modern practice, but I haven't done a thorough job of finding all of them. Finally, one amusing side effect of doing this is that I've had to separate quite a few chained assignments. It used to be perfectly fine to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a the 'true' defined by stdbool.h, that idiom provokes a warning from gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 19:23:19 +00:00
bool run_timers(unsigned long anow, unsigned long *next)
{
struct timer *first;
init_timers();
now = GETTICKCOUNT();
while (1) {
first = (struct timer *)index234(timers, 0);
if (!first)
return false; /* no timers remaining */
if (find234(timer_contexts, first->ctx, NULL) == NULL) {
/*
* This timer belongs to a context that has been
* expired. Delete it without running.
*/
delpos234(timers, 0);
sfree(first);
} 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.
*/
delpos234(timers, 0);
first->fn(first->ctx, first->now);
sfree(first);
} else {
/*
* This is the first still-active timer that is in the
* future. Return how long it has yet to go.
*/
*next = first->now;
return true;
}
}
}
/*
* Call to expire all timers associated with a given context.
*/
void expire_timer_context(void *ctx)
{
init_timers();
/*
* We don't bother to check the return value; if the context
* already wasn't in the tree (presumably because no timers
* ever actually got scheduled for it) then that's fine and we
* simply don't need to do anything.
*/
del234(timer_contexts, ctx);
}