Le vendredi 9 juillet 2021 à 14:31:35 UTC+2, Albrecht Schlosser a écrit :
On 7/9/21 11:11 AM Manolo wrote:
Le mercredi 7 juillet 2021 à 16:45:59 UTC+2, Albrecht Schlosser a
écrit :
Before actually working on the new proposal I tried to fix two
inconsistencies of the macOS platform. I committed one fix
(87475c20d6cc81912e) and created PR #248. Manolo, could you
please
review the PR?
OK with this change.
Thanks for confirmation. As you may have noticed I merged and closed
this PR.
I'm uncertain about what is expected by
Fl::repeat_timeout().
Fl::repeat_timeout() is exactly the same as Fl::add_timeout() with
at "correction" that takes into account if the last timeout (i.e.
the current timeout callback) expired (a little) too late. In the
theory this should allow "for more accurate timing" as the docs say.
The example code that "will print 'TICK' each second ... with a fair
degree of accuracy" demonstrates its intention. The Unix
implementation tries to calculate a delay of the time when
the timer expired as opposed to when it was expected to expire
(variable: missed_timeout_by) and corrects the delay by that amount,
i.e. if the current timer was "missed" by 10 ms then
Fl::repeat_timeout(1.0) should schedule a new timeout at 1.0s - 10
ms from "now". The meaning of "expired" above is the time when FLTK
got control of the timeout, i.e. somewhere in the function
elapse_timeouts(). If the current delta is negative, then we missed
the scheduled timeout by that amount.
A good measure of this behavior would be to calculate the average
delay of a sequence of, say, 50 timeouts of 1.0 seconds. If this
algorithm works well, the average would be pretty exact 1.0.
This "correction" is done on the Unix/Linux platform but IIRC not on
Windows. I'm not sure about macOS, something I need to investigate
further (with your help).
The overall (platform independent) behavior of Fl::repeat_timeout()
is something I'd like to address in the new, platform independent,
timeout code.
Its general goal is to schedule a new timeout at a given
delay (δ) after the previous timeout
(last_t) was scheduled. My question is "what should
Fl::repeat_timeout() do when it runs after this
delay expired (after last_t + δ) ?". The current
implementation for the macOS platform prioritizes the
regularity
of timeouts, and schedules a new timeout for last_t + n * δ
where n is the smallest integer value that puts this date in
the future.
As you describe it, I believe the goal is the same, but the
implementation might be slightly different.
I believe the Unix platform implementation prioritizes the
running of timeout callbacks and
has the callback run several times without delay.
Hmm, I don't understand what you mean with this sentence. There's no
repetition in Fl::repeat_timeout() itself, it's just one (next)
timeout that is to be scheduled.
Let's make an example. To be more realistic, let the delay (δ) be
0.1 sec, i.e. Fl::repeat_timeout(0.1, ...), and the first timeout is
triggered at time 10.0 sec. I'll list the sequence below line by
line
10.00 Fl::repeat_timeout(0.1) -> delay 0.10 for next timeout
timer code runs a little late due to system load:
10.12 (missed_timeout_by = 0.02), Fl::repeat_timeout(0.1) ->
delay 0.08 for next timeout (should be 10.20)
Now, is your question what happens if the next timeout expiration is
run by FLTK much later, at:
10.31 (missed_timeout_by = 0.11), Fl::repeat_timeout(0.1),
correction by -0.11 yields -0.01 delay, i.e. the next timer schedule
has already passed?
Yes. That's exactly my question. What to do in that situation?
I think in this code the Unix/Linux would schedule the next timeout
immediately so we don't miss a timeout.
That's what I believe Unix does too.
Do you say that the macOS code would schedule the next timeout at
10.40? Is this the difference you're talking about?
Yes.
If this was the case, then the average of timer delays would suffer
significantly (by 0.1/n), whereas the Unix implementation would only
"drift" once by 0.01 seconds and the average would increase by
0.01/n).
In terms of average delay, I agree skipping is bad.
But in terms of rythm, having 2 iterations without delay in between is very bad.
What do FLTK developers believe should be the priority of
Fl::repeat_timeout()?
My personal opinion is that the next timeout should be sheduled as
soon as possible if the calculated "next" timeout has already passed
(if I understood your question).
Fl::repeat_timeout() should be triggered as exact as possible after
the point in time where the last (current) timeout should have been
triggered plus the delay given as the argument to "allow for more
accurate timing", as the docs express it.
In other words: the above described sequence of n timeouts should
not "drift away" as it probably does on Windows in our current
implementation because there's no correction applied. I had planned
to write such a demo program anyway. I'll do this shortly and post
it here.
When the timeout is just a little bit late, say by delta, the correct solution is clear: schedule the next timeout to now + delay - delta.
My question arises when the timeout is very late, more than the delay between successive timeouts. What to do in that situation?
Either :
- skip one iteration, because its time is over, and schedule for next iteration;
- play two iterations without delay in between.
Both choices avoid the drift seen with the Windows implementation.