-
Couldn't load subscription status.
- Fork 9
Make the timer generic and support periodic timers #79
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Make the timer generic and support periodic timers #79
Conversation
|
I'm marking this as a draft only because it is lacking tests, but it should be ready to review (I did some basic testing manually). I would like to get feedback before spending too much time with tests (as I would like to introduce also |
ad39385 to
a9b53a4
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks good to me. If we don't go the consolidating TimerTick route I mentioned, we might want to add a docstring line here explaining the difference from utils.Timer, and a line in utils.Timer saying when people should use a PeriodicTimer instead.
6189558 to
85f016e
Compare
|
Updated according to what we talked about. So now I didn't include the The tests are failing because of styling issues, it would be good if you can review it @sahas-subramanian-frequenz to see if you are good with it now so I can finish the docs, PR and removing |
6a09617 to
ce36bab
Compare
564949b to
ab02b44
Compare
|
Fixed some bugs and added some tests. I need to add some more but I consider this almost done. Not ready for merging because of the above, but not a draft anymore either. |
|
Since nobody looked at this yet, I'm converting to draft again because when doing more tests and using I also don't like that So a couple of major changes are coming. |
ab02b44 to
a9912a6
Compare
|
This PR is now ready for review for real. There are still quite a few commits that could be squashed together, but since this PR took so much work to get right (still to be seen :D), I'd prefer to keep the history as is, as it shows better the problems found while developing it. I even wonder if the problems found here with Because of the above, I recommend to only go commit-by-commit to have a quick look to see if commits are consistent, but for the logic, it is probably better to review the final diff, because both the way the timer presents time internally and the missing tick policies were completely rewritten in the PR itself. |
The current timer provided is very convenient to implement timers, but it's not very helpful to implement a peridic timer that doesn't accumulate drift. This commit adds a `util.PeriodicTimer` that ensures that drift is not accumulated and that the receiver will be ready exactly after one `interval` has passed (not counting any delay introduced by the event loop being busy with another task). It is also possible to control when this timer is started, and can be stopped and reset/restarted. Signed-off-by: Leandro Lucarella <[email protected]>
The `PeriodicTimer` can now be created using different ways to deal with missing ticks, including what the current `Timer` does. Signed-off-by: Leandro Lucarella <[email protected]>
`Timer` is replaced by more flexible `PeriodicTimer`. Signed-off-by: Leandro Lucarella <[email protected]>
According to https://taplo.tamasfe.dev/. Signed-off-by: Leandro Lucarella <[email protected]>
This is the most sensible default for this tick behaviour. At some point we might want to refactor this so behaviours are classes instead of an enum, so they can take their own options (with the defaults that make more sense) and even calculate the next tick on their own. Signed-off-by: Leandro Lucarella <[email protected]>
The ready() method got too long, so it makes sense to split the missing tick handling to a separate method. Signed-off-by: Leandro Lucarella <[email protected]>
There are several advantages of using classes instead of enum for the missed tick behavior: * The `delay_tolerance` option doesn't make sense for all behavior, so now each behavior can have its own parameters * New behaviors can be implemented without having to modify the `PeriodicTimer` * Easier to test This commit doesn't update the tests because when trying to update them and use the `hypothesis` package to test the different behaviors, it was evident that using floats to represent times is a recipe for disaster, specially when the monotonic clock is way bigger than the interval. For behaviors when more math than a simple addition is needed (for example, modulo), it is very likely to have wrong calculations on those circumstances. It is even necessary to adapt the current tests to use a bit less than the tolerance in some case to cope with floating point errors. A following commit will use `int`s internally to represent the time, intervals and drifts. Signed-off-by: Leandro Lucarella <[email protected]>
As pointed out in the previous commit, using float to represent time can lead to extremely hard to debug issues in cases where the magnitude of the amounts to compare or operate with, or the amount of relevant digits are very different. To avoid this we use ints to represent time internally, all in microseconds. Signed-off-by: Leandro Lucarella <[email protected]>
This is a aesthetic change, just to group both argument that represent times (a tick happened and should have happened). Signed-off-by: Leandro Lucarella <[email protected]>
The name behavior was inspired by Rust's Tokio timers (Interval[1]), but it doesn't sound very nicely when talking about a "behavior". A policy seems to be a better match for this. [1] https://docs.rs/tokio/latest/tokio/time/fn.interval.html Signed-off-by: Leandro Lucarella <[email protected]>
Signed-off-by: Leandro Lucarella <[email protected]>
Otherwise nasty things can happen. Signed-off-by: Leandro Lucarella <[email protected]>
We use the hypothesis library to test policies more thoroughly and add some more tests for timer construction and most notably a test for a timer using the `SkipMissedAndResync` policy. This commit also include some minor naming and documention improvements in existing tests. Signed-off-by: Leandro Lucarella <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I only have a cosmetic comment to check for and LGTM. Really good work with the documentation and examples which helps a lot to the review process, also the changes well separated in different commits
The current implementation is really not a periodic timer, that's determined by the used missed ticks policy, so it makes sense to go back to call it just Timer. Signed-off-by: Leandro Lucarella <[email protected]>
Because now the timer is generic, it doesn't make a lot of sense to provide a default policy for missing ticks. The argument is also now a positional-only argument, as is not optional and has a very specific type it makes more sense to avoid the keyword argument. Signed-off-by: Leandro Lucarella <[email protected]>
The syntax is `f(pos_only, /)`, not `f(/, pos_only)`. Signed-off-by: Leandro Lucarella <[email protected]>
This commit adds two new constructors: `timeout(delay)` and `periodic(period, skip_missed_ticks=False)` as these are the most common use cases. This should provide a more user friendly experience for the vast majority of users. Signed-off-by: Leandro Lucarella <[email protected]>
Add missing changes and a summary. Signed-off-by: Leandro Lucarella <[email protected]>
6e11161 to
390439f
Compare
|
Updated to remove all mentions to castrated ram (wether) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the new naming scheme so much better than PeriodicTimer 🎉
|
I know, and me too. Thanks for pushing back! |
This pull request have 2 different but related main changes: * The `FileWatcher` was skipping `DELETE` events, except on very weird cases where the file was deleted and re-created just before the event was triggered by `awatch()`. This also extends the integration test to test both situations (delete events when a file exist and when it doesn't). Because there is usually a considerable delay between a change in the filesystem happens and `awatch()` emits an event it is difficult to decide if we should filter events based on the file existing or not. For example, if a create event is received but before is triggered the file was removed, should we emit the create event or not? Or the other way around, should we emit a delete event if a file was created again before the event was triggered. It is probably best to leave this decision to the user, and let them deal with races. * `FileWatcher` tests were rearranged and split between unit and integration tests. The existing tests were really integration tests, as they are not only testing `FileWatcher`, but also `Select` and `PeriodicTimer`, which is confusing when there is a bug in any of the other classes as we might get test failures here (this actually happened during the development of #79). Because of this the current tests were moved to `test/util/test_integration.py`. We also mark the tests with the `pytest` mark `integration` to make it more clear they are integration tests, and to enable an easy way to only run (exclude) integration tests. New unit tests were added to the current test file, properly mocking `awatch` to avoid interactions with the OS. There are many more integration tests posing as unit tests, but this PR only takes care of `FileWatcher`. With time we should move the rest too.
The current timer provided is very convenient to implement timers, but it's not very helpful to implement a peridic timer that doesn't accumulate drift.
This PR makes the timer configurable via missing ticks policies and adds 3 policies:
TriggerAllMissing,SkipMissingAndResyncandSkipMissingAndDrift.For convenience, 2 alternative constructors are provided for the most common cases:
Timer.timeout()andTimer.periodic(). Timeout timers always drift and periodic timers never.The new
Timeralso uses theasyncioloop monotonic clock, and returns thedrift(difference between when the timer should have triggered and it actually did) instead of adatetimewith the time of trigger.It is also now possible to control when this timer is started, and can be stopped and reset/restarted at will.
Fixes #78.