Skip to content

Conversation

jmarantz
Copy link
Contributor

@jmarantz jmarantz commented Sep 5, 2018

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

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>
zuercher
zuercher previously approved these changes Sep 10, 2018
Copy link
Member

@zuercher zuercher left a 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
Copy link
Member

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().
Copy link
Member

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.
Copy link
Member

@zuercher zuercher Sep 10, 2018

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

Copy link
Contributor Author

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
Copy link
Member

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?

Copy link
Contributor Author

@jmarantz jmarantz Sep 10, 2018

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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: adjustments

Copy link
Contributor Author

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.
Copy link
Member

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"?

Copy link
Contributor Author

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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: capitalize if

Copy link
Contributor Author

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>
zuercher
zuercher previously approved these changes Sep 10, 2018
Copy link
Member

@zuercher zuercher left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

@zuercher
Copy link
Member

@junr03 can you have a look at this one just for a second set of eyes?

Copy link
Member

@junr03 junr03 left a 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;
Copy link
Member

@junr03 junr03 Sep 10, 2018

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.

Copy link
Contributor Author

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;
Copy link
Member

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++.

Copy link
Contributor Author

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.

Copy link
Member

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?

Copy link
Contributor Author

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.

Copy link
Member

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.

Copy link
Member

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?

Copy link
Contributor Author

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()) {
Copy link
Member

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.

Copy link
Contributor Author

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.

Copy link
Member

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_);
Copy link
Member

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.

Copy link
Contributor Author

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?

Copy link
Member

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.

zuercher pushed a commit that referenced this pull request Sep 11, 2018
This makes it possible to incorporate simulated time (#4340) into integration tests, driving toward resolution of #4160 .

Risk Level: low
Testing: //test/...
Docs Changes: n/a
Release Notes: n/a

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>
@@ -10,7 +10,6 @@ jobs:
resource_class: xlarge
working_directory: /source
environment:
BAZEL_REMOTE_CACHE: https://storage.googleapis.com/envoy-circleci-bazel-cache/
Copy link
Member

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.

Copy link
Contributor Author

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

Copy link
Contributor Author

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>
@htuch htuch merged commit 03e714e into envoyproxy:master Sep 17, 2018
@jmarantz jmarantz deleted the sim-time branch September 17, 2018 18:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

integration tests should use simulated time
4 participants