-
Notifications
You must be signed in to change notification settings - Fork 5.1k
test: Create SimulateTimeSystem for running tests #4340
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
Conversation
Signed-off-by: Joshua Marantz <jmarantz@google.com>
Signed-off-by: Joshua Marantz <jmarantz@google.com>
Signed-off-by: Joshua Marantz <jmarantz@google.com>
Signed-off-by: Joshua Marantz <jmarantz@google.com>
Signed-off-by: Joshua Marantz <jmarantz@google.com>
…nd monotonic-time. Remove a NUL char. Signed-off-by: Joshua Marantz <jmarantz@google.com>
…s(n) Signed-off-by: Joshua Marantz <jmarantz@google.com>
… for that. Signed-off-by: Joshua Marantz <jmarantz@google.com>
Signed-off-by: Joshua Marantz <jmarantz@google.com>
Signed-off-by: Joshua Marantz <jmarantz@google.com>
…calling Dispatcher::run() should be calling the timer callbacks. Signed-off-by: Joshua Marantz <jmarantz@google.com>
Signed-off-by: Joshua Marantz <jmarantz@google.com>
Signed-off-by: Joshua Marantz <jmarantz@google.com>
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 nits on comments, but otherwise looks great.
|
||
// Our simulated alarm inherits from TimerImpl so that the same dispatching | ||
// mechanism used in RealTimeSystem timers is employed for simulated alarms. | ||
// using libevent's event_active(). Note that libevent is placed into |
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.
nit: I think you can just delete the "using libevent's event_active()" bit.
|
||
/** | ||
* Activates the timer so it will be run the next time the libevent loop is run, | ||
* typiically via Dispatcher::run(). |
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.
nit: typically
// Compare two alarms, based on wakeup time and insertion order. Returns true if | ||
// a comes before b. | ||
|
||
// like strcmp (<0 for a < b, >0 for a > b), based on wakeup time and index. |
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.
nit: rm strcmp reference and blank line
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.
done
return false; | ||
}; | ||
|
||
// Each scheduler maintains its own timer |
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.
nit: time system instead of timer?
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 TimeSystem is common across all the schedulers. At one point I thought each Scheduler would maintain its own set of timers, but it turns out to be easier to have the TimeSystem own the timers for every scheduler. The scheduler just holds onto the libevent_ base ptr, to allow activation when time moves forward. I'll add more commenting.
Reworded as:
// Each timer is maintained and ordered by a common TimeSystem, but is
// associated with a scheduler. The scheduler creates the timers with a libevent
// context, so that the timer callbacks can be executed via Dispatcher::run() in
// the expected thread.
// Represents a simulated time system, where time is advanced by calling | ||
// sleep(), setSystemTime(), or setMonotonicTime(). systemTime() and | ||
// monotonicTime() are maintained in the class, and alarms are fired in response | ||
// to adjsutments in time. |
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.
nit: adjustments
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.
done
* along the way that have been scheduled to fire. | ||
* | ||
* @param duration The amount of time to sleep, expressed in any type that | ||
* can be added to duration-cast to MonotonicTime::duration. |
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.
nit: I had a hard time parsing this, maybe switch the hypen to "when"?
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.
reworded; to:
* @param duration The amount of time to sleep, expressed in any type that
* can be duration_casted to MonotonicTime::duration.
} | ||
|
||
/** | ||
* Sets the time forward monotonically. if the supplied argument moves |
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.
nit: capitalize if
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.
done
Signed-off-by: Joshua Marantz <jmarantz@google.com>
Signed-off-by: Joshua Marantz <jmarantz@google.com>
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.
Thanks!
@junr03 can you have a look at this one just for a second set of eyes? |
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.
a couple comments
|
||
private: | ||
class SimulatedScheduler; | ||
friend class SimulatedScheduler; |
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.
both here and below the friend class declaration is not needed. I just learned that since C++11 inner classes are automatically friends of the outer class.
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.
nice...that is useful. done.
private: | ||
class SimulatedScheduler; | ||
friend class SimulatedScheduler; | ||
class Alarm; |
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.
just wondering about the choice of forward declaring these two classes and then defining them in the cc file rather than the header file. I am not sure which is canonical c++.
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.
Not sure about whether it is idiomatic...I have a practice of trying to put impl details into the cc where possible and not impactful to performance.
IMO that helps hide unimportant details from the user of the simulated time system, keep the impl details together, and minimize recompiles when changing impl.
But i can put them here if it is a strong envoy convention.
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.
That seems to me like a fair assessment. Good to know the intention, and maybe something I can adopt in the future.
I mentioned it because in general what I have seen in the envoy codebase is the other way around, where, if small enough, implementation is brought into the header file. But I don't think there is a hard and fast rule.
@alyssawilk or @htuch just wondering if you lean in one way or another in this topic?
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.
btw based on past experience, I think Envoy coding style of inling virtual dtors may contribute to slow link-time for test binaries, which we have a lot of.
In Chrome coding style this inlining of all ctors/dtors is discouraged, though IMO the virtual dtors are more of an issue.
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 other consideration is rebuild time; if you have to make a change to a .h
that is imported in 100 places you have 100 rebuilds, vs. the single .cc
. I agree with @junr03 that we have a habit of putting small implementations in .h
. I think this is because (i) it's less typing and (ii) more readable. I think link and rebuild time is a good argument for preferring .cc
, but no strong preference.
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.
Do we really need to do as much forward declaration here? Is this to break a cycle?
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.
No, it's to declare friend classes that only need to be ref'd internal to the impl.
// That can only happen here in alarm->activate(), which is run with the mutex | ||
// released. | ||
if (monotonic_time >= monotonic_time_) { | ||
while (!alarms_.empty()) { |
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.
nit: not sure if it is worth it or not. But I think this might be confusing to a new pair of eyes. It involves understanding that sets iterate in sorted order (and thanks to your overloaded operator this works), that we are modifying the set as we go, and that we are also moving time. I wonder if a comment spelling this out a bit would be helpful for others.
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.
Added comment
// Alarms is a std::set ordered by wakeup time, so pulling off begin() each
// iteration gives you wakeup order. Also note that alarms may be added
// or removed during the call to activate() so it would not be correct to
// range-iterate over the set.
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.
clear, thanks!
}; | ||
typedef std::set<Alarm*, CompareAlarms> AlarmSet; | ||
|
||
void setMonotonicTimeAndUnlock(const MonotonicTime& monotonic_time) UNLOCK_FUNCTION(mutex_); |
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 function also changes system time, I wonder if we should rename.
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.
Do you have a suggestion? Above I have the same issue 2x...maybe just a comment suffices?
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, it is kind of funny because the type of time in the function name is not what you are setting, but what you are setting both times with... I guess in my mind setX
is a function that sets X not a function that uses X to set other things.
Sorry, don't want to split too many hairs, so perhaps just a comment like the above suffices as you suggest.
Signed-off-by: Joshua Marantz <jmarantz@google.com>
Signed-off-by: Joshua Marantz <jmarantz@google.com>
Signed-off-by: Joshua Marantz <jmarantz@google.com>
.circleci/config.yml
Outdated
@@ -10,7 +10,6 @@ jobs: | |||
resource_class: xlarge | |||
working_directory: /source | |||
environment: | |||
BAZEL_REMOTE_CACHE: https://storage.googleapis.com/envoy-circleci-bazel-cache/ |
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 don't think this was related to the failures you saw. Let's make sure we revert this before merge.
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.
agreed. but it won't pass CI without it, afaict
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.
Looks like CI is starting to work again without removing the remote cache.
Signed-off-by: Joshua Marantz <jmarantz@google.com>
Signed-off-by: Joshua Marantz <jmarantz@google.com>
Description: Add an alternate TimeSystem implementation which keeps manually-adjusted time and timer-callbacks making sense together. This is intended for use in most if not all unit tests, and many integration tests. This is another step to resolve #4160.
Risk Level: low -- this is a new test-only class that's not used anywhere yet.
Testing: //test/...
Docs Changes: n/a
Release Notes: n/a