mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-09 17:38:00 +00:00
Add a lengthy comment warning future front-end implementors about
the right and wrong way to implement the timing interface. [originally from svn r5137]
This commit is contained in:
parent
75008d9da0
commit
b4b415e546
61
putty.h
61
putty.h
@ -993,6 +993,67 @@ char *get_random_data(int bytes); /* used in cmdgen.c */
|
||||
* notifies the front end that a new timer has been added to the
|
||||
* list which is sooner than any existing ones. It provides the
|
||||
* time when that timer needs to go off.
|
||||
*
|
||||
* *** FRONT END IMPLEMENTORS NOTE:
|
||||
*
|
||||
* There's an important subtlety in the front-end implementation of
|
||||
* the timer interface. When a front end is given a `next' value,
|
||||
* either returned from run_timers() or via timer_change_notify(),
|
||||
* it should ensure that it really passes _that value_ as the `now'
|
||||
* parameter to its next run_timers call. It should _not_ simply
|
||||
* call GETTICKCOUNT() to get the `now' parameter when invoking
|
||||
* run_timers().
|
||||
*
|
||||
* The reason for this is that an OS's system clock might not agree
|
||||
* exactly with the timing mechanisms it supplies to wait for a
|
||||
* given interval. I'll illustrate this by the simple example of
|
||||
* Unix Plink, which uses timeouts to select() in a way which for
|
||||
* these purposes can simply be considered to be a wait() function.
|
||||
* Suppose, for the sake of argument, that this wait() function
|
||||
* tends to return early by 1%. Then a possible sequence of actions
|
||||
* is:
|
||||
*
|
||||
* - run_timers() tells the front end that the next timer firing
|
||||
* is 10000ms from now.
|
||||
* - Front end calls wait(10000ms), but according to
|
||||
* GETTICKCOUNT() it has only waited for 9900ms.
|
||||
* - Front end calls run_timers() again, passing time T-100ms as
|
||||
* `now'.
|
||||
* - run_timers() does nothing, and says the next timer firing is
|
||||
* still 100ms from now.
|
||||
* - Front end calls wait(100ms), which only waits for 99ms.
|
||||
* - Front end calls run_timers() yet again, passing time T-1ms.
|
||||
* - run_timers() says there's still 1ms to wait.
|
||||
* - Front end calls wait(1ms).
|
||||
*
|
||||
* If you're _lucky_ at this point, wait(1ms) will actually wait
|
||||
* for 1ms and you'll only have woken the program up three times.
|
||||
* If you're unlucky, wait(1ms) might do nothing at all due to
|
||||
* being below some minimum threshold, and you might find your
|
||||
* program spends the whole of the last millisecond tight-looping
|
||||
* between wait() and run_timers().
|
||||
*
|
||||
* Instead, what you should do is to _save_ the precise `next'
|
||||
* value provided by run_timers() or via timer_change_notify(), and
|
||||
* use that precise value as the input to the next run_timers()
|
||||
* call. So:
|
||||
*
|
||||
* - run_timers() tells the front end that the next timer firing
|
||||
* is at time T, 10000ms from now.
|
||||
* - Front end calls wait(10000ms).
|
||||
* - Front end then immediately calls run_timers() and passes it
|
||||
* time T, without stopping to check GETTICKCOUNT() at all.
|
||||
*
|
||||
* This guarantees that the program wakes up only as many times as
|
||||
* there are actual timer actions to be taken, and that the timing
|
||||
* mechanism will never send it into a tight loop.
|
||||
*
|
||||
* (It does also mean that the timer action in the above example
|
||||
* will occur 100ms early, but this is not generally critical. And
|
||||
* the hypothetical 1% error in wait() will be partially corrected
|
||||
* for anyway when, _after_ run_timers() returns, you call
|
||||
* GETTICKCOUNT() and compare the result with the returned `next'
|
||||
* value to find out how long you have to make your next wait().)
|
||||
*/
|
||||
typedef void (*timer_fn_t)(void *ctx, long now);
|
||||
long schedule_timer(int ticks, timer_fn_t fn, void *ctx);
|
||||
|
Loading…
Reference in New Issue
Block a user