clock: ensure that updates happen on unit boundaries #66
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
The Problem
In the above diagram, the times are minute boundaries. If the bar is started at A, it will check the time and correctly display
00:00
(assuming the format string%H:%M
). However, it will then sleep for (just under) a minute, and it won't display00:01
until point B. If point A is closer to the end of its minute, the clock could be almost a full minute off.The Solution
First, I added a generic to the
Clock
struct, allowing for different levels ofPrecision
. There are currently four options:Days
,Hours
,Minutes
, andSeconds
. I replaced the clock widget'sIntervalStream
with a customClockStream
. It also holds an internalInterval
, but when the interval completes, theStream
updates theInterval
's deadline to the next boundary of theClock
's precision. It's impossible to use the same generic on theClockStream
struct because we need distinct implementations ofpoll_next()
, and we can't prove to Rust that we've implementedStream
exhaustively without something likeimpl<P: Precision> Stream for ClockStream<P>
. The solution is to separate the part of that implementation and pass it to theClockStream
constructor in the form of an anonymous function.Notes
I tried many options before arriving at this one, so my explanation might not be clear. Please let me know if you have any questions!
I did have to bump tokio to version 1.30.0 to gain access to the
reset_at()
function on Interval. I'm not sure what standard practice is here, but any version of tokio between 1.30.0 and 1.37.0 (the latest version as I write this) should work. I'm happy to change theCargo.toml
if necessary.