-
Notifications
You must be signed in to change notification settings - Fork 37.7k
refactor: Make FEELER_SLEEP_WINDOW type safe (std::chrono) #24974
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
The head ref may contain hidden characters: "2204-feeler-type-\u{1F434}"
Conversation
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. ConflictsReviewers, this pull request conflicts with the following ones:
If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first. |
ACK fae94c6 |
@ajtowns You might be interested in (N)ACKing this change, based on the conversation in #24697 (comment) |
fa7ca1e
to
fa9970a
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.
Code Review ACK fa9970a
fa9970a
to
fac6af7
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.
reACK fac6af7
{ | ||
using Dur = typename Chrono::duration; | ||
return range.count() > 0 ? /* interval [0..range) */ Dur{randrange(range.count())} : | ||
range.count() < 0 ? /* interval (range..0] */ -Dur{randrange(-range.count())} : |
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.
Why support negative ranges?
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.
Some call sites may use a negative range. For example in addr relay, the timestamp of some address messages is randomly pushed into the past.
Also, it seems safer when the alternative is either unspecified behaviour, or a crash.
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.
The behavior here still seems odd in those cases. I'd expect call sites that actually can result in negative range values to want "rand_uniform_duration(T)" to mean: "give me a uniform duration not exceeding T". If T is negative, that probably means just returning 0?
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.
Yeah, the interval is determined at run-time and the passed in value can either mean the start or the end of the interval, with the other value being 0
.
I am not sure if mapping any negative value to a constant 0
is less confusing. And I couldn't find another solution that is well defined at run time (no unspecified behaviour, no crash).
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.
Yeah, I'm not objecting to this change. It just strikes me as bizarre semantics; the simplest interpretation of a range is specifying how wide the window of possible values is - and that simply cannot be negative.
An alternative is always returning range itself if it is <= 0. That at least satisfies the constraint "never return a value exceeding range", and probably works just as well for the intended call sites (who I suspect use to determine how long to wait)?
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 think there is a minimal, theoretical advantage to disallowing negative values, as the inverting the most negative value is an integer sanitizer violation. Though, this shouldn't affect execution of a program that is not compiled with the integer sanitizer and shouldn't happen in practise either.
What is the "C++20" syntax, if I may ask?
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.
With C++20 you can pass a duration to a template, and then do a static_assert on its value, so you'd write rand_uniform_duration<dur>()
instead of rand_uniform_duration(dur)
and get the compile-time check.
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.
Oh, with dur
you mean static_duration
, not std::chrono::duration
?
I can't get it to work with std::chrono::duration
:
#include <chrono>
using namespace std::chrono_literals;
struct FRC {
template <std::chrono::duration range>
auto rand_uniform_delay(auto time) {
return time + rand_uniform_duration<decltype(time)::duration, range>();
}
template <typename Duration, std::chrono::duration range>
Duration rand_uniform_duration() noexcept {
static_assert(range.count() > 0);
return Duration{randrange(range.count())};
}
};
int main() {
FRC frc;
frc.rand_uniform_delay<9h>(std::chrono::sys_seconds{1s});
frc.rand_uniform_duration<std::chrono::seconds, 9h>();
}
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.
Hmm, I'm not really sure why it doesn't work (well, std::chrono::duration
needs template parameters and stuff, but even after fixing that it doesn't work)
Here's something that does, via a wrapper class. Not sure that's any better than @theuni's c++17 compatible wrapper. https://godbolt.org/z/nnKx4fde5
#include <chrono>
int64_t randrange(int64_t x) { return x / 2; }
using namespace std::chrono_literals;
template<typename dur=std::chrono::seconds>
struct template_duration
{
const int64_t count;
using duration = dur;
constexpr template_duration(dur f) : count{f.count()} { }
constexpr dur to_dur() const { return dur{count}; }
};
template<typename dur>
constexpr template_duration<dur> tmpl_dur(const dur& d) { return {d}; }
struct FRC {
template <typename Duration, auto range>
Duration rand_uniform_duration() noexcept {
static_assert(range.count > 0);
return Duration{randrange(Duration{range.to_dur()}.count())};
}
template <auto range, typename X>
X rand_uniform_delay(X time) noexcept {
return time + rand_uniform_duration<typename X::duration, range>();
}
};
int main() {
FRC frc;
constexpr auto DELAY_HOURS{tmpl_dur(8h)};
auto now = std::chrono::sys_seconds{1s};
frc.rand_uniform_duration<std::chrono::seconds, DELAY_HOURS>();
frc.rand_uniform_delay<DELAY_HOURS>(now);
}
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.
Happy to use different std-lib syntax, but anything that requires a wrapper class that re-implements stuff seems overkill to solve a problem that doesn't exist.
@@ -19,13 +19,12 @@ | |||
class CThreadInterrupt | |||
{ | |||
public: | |||
using Clock = std::chrono::steady_clock; |
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.
review note: https://eel.is/c++draft/thread.condition.condvar#24
fac6af7
to
fabfede
Compare
Rebased only for now |
Overloading sleep_for is not needed, as * seconds and minutes can be converted to milliseconds by the compiler, not needing a duration_cast * std::condition_variable::wait_for will convert milliseconds to the duration type of the underlying clock So simply expose the clock.
fabfede
to
fa74e72
Compare
Rebased |
utACK fa74e72 |
Code review ACK fa74e72 |
…d::chrono) fa74e72 refactor: Make FEELER_SLEEP_WINDOW type safe (std::chrono) (MacroFake) fa3b3cb Expose underlying clock in CThreadInterrupt (MacroFake) Pull request description: This gets rid of the `value*1000` manual conversion. ACKs for top commit: naumenkogs: utACK fa74e72 dergoegge: Code review ACK fa74e72 Tree-SHA512: 90409c05c25f0dd2f1c4dead78f707ebfd78b7d84ea4db9fcefd9c4958a1a3338ac657cd9e99eb8b47d52d4485fa3c947dce4ee1559fb56ae65878685e1ed9a3
This gets rid of the
value*1000
manual conversion.