Author: Alexandre Ferrieux <[email protected]>
Author: Kevin Kenny <[email protected]>
Keywords: Tcl,time changes
Currently, after tasks can be drastically delayed or sped up simply by adjusting the system clock. This is due to the implementation's use of an absolute target date based on the system clock. The idea of this TIP is to use another timebase for this purpose: the count of ticks from boot, which is not affected by system time adjustments.
The basis of the implementation of after is, on each call to vwait, to compute the timeout argument to select() by difference between the stored target date of the earliest event and the current system date (gettimeofday()). This is perfect while the system date ticks regularly. But if, say, the clock is set back by 5 minutes, then an after handler scheduled 1 second ago which was just about to fire, will have 5 minutes yet to wait before its (unmodified) target date is reached. So, if this handler was part of a 1-Hz periodic task, there will be a huge gap of 5:01 between two ticks at that point. If some other component is expecting some kind of regularity, even with a conservative timeout of 10 times the expected period, it will time out, decide the periodic task is dead, and possibly take drastic action.
This TIP proposes to use other timebases instead of gettimeofday() in the vwait/after code: times() in unix, GetTickCount() in Windows. These clocks suffer no sysadmin tinkering.
Potential Break Of Compatibility
It has been objected that some applications today may be using after with an absolute spirit; IOW such apps are supposed to rely on the fact that the after handler will fire when the system clock equals the target date computed once for all when after was called. A prototype example would be a crontab-like task, which would itself compute the offset by difference between the target date and the current clock seconds.
Arguments For Breaking It Anyway
This absolute interpretation is far from being natural, because after's argument is an offset.
This technique is not usable for a longer range than 25 days (MAXINT milliseconds), so not applicable e.g. for a personal schedule.
The overwhelming majority of uses of after takes the relative interpretation (periodic tasks, timeouts) and cannot work correctly today.
If this TIP were implemented incompatibly (i.e. without a specific flag to after), those absolute-minded apps could simply be adapted and improved in both robustness and range by using a periodic task which polls clock seconds.
There is little evidence that the total number of absolute-minded apps exceeds one (see discussion on news:comp.lang.tcl "[after] fooled by shifting date")
Syntax For Not Breaking It If Deemed Useful
Of course, if this supposed singleton is in fact many, or has enough weight to preclude an improvement of the rest of Tcl timers, we can do:
after -robust millisecs
or any other colorful option name. But in this case there is a high risk that: either after -robust becomes the dominant use, thus cluttering the code in many places, or people remain largely unaware of the problem, stick to the default after, and space shuttles fall by dozens.
Escalation To The TCT
I'll leave it to the TCT to arbitrate, and decide whether fixing a widely used core primitive can outweigh breaking a rare and clumsy use.
I have not yet written a reference implementation; I assume somebody with a more fluent practice of the core will do so more efficiently. However, gentle arm twisting, etc.
As mentioned 2021-09-01 by Christian Werner, Androwish/Undroidwish has a reference implementation for Android and Unix.
As mentioned 2021-09-03 by Sergey Blester, the following TCL branch contains a well tested and solid solution for Unix and Windows: https://core.tcl-lang.org/tcl/tktview/fdfbd5e10fefdb605abf34f65535054c323d9394
This document has been placed in the public domain.
The times function in Unix is not an appropriate time base. It reports the user and system time (CPU time, in other words) of the currently executing process and its children. As far as I have been able to determine, Unix assumes that the system time reported by gettimeofday is the sole time base for absolute timing; if multiple timers are required in a single process, gettimeofday appears to be the only reference that is available.