Commit a2792966 authored by Keivaun Waugh's avatar Keivaun Waugh Committed by Facebook GitHub Bot

Add callback for single EVB loop duration violators

Summary:
Context for motivation is in
https://fb.workplace.com/groups/560979627394613/permalink/2194915787334314.
This solution is different than the one we discussed in that post. I considered
implementing a set of post-evb-loop function callbacks that all took in the
ebv loop_time, but that was a bigger change than expected since currently the
callbacks are designed to not take any variables, and modifying/adding classes
like `FunctionLoopCallback` to either take a param or not seemed like overkill.
I instead implemented the callback basically the same as setMaxLatency
exception that the duration is a single-loop duration with no exponential
smoothing or backoffs.

Differential Revision: D30916498

fbshipit-source-id: 27cc7edf919ab11d2ee75a7f722b6a48b193c445
parent bb46a529
......@@ -1260,6 +1260,8 @@ REGISTER_TYPED_TEST_CASE_P(
CallbackOrderTest,
AlwaysEnqueueCallbackOrderTest,
IdleTime,
MaxLatencyUndamped,
UnsetMaxLatencyUndamped,
ThisLoop,
EventBaseThreadLoop,
EventBaseThreadName,
......
......@@ -413,13 +413,18 @@ bool EventBase::loopBody(int flags, bool ignoreKeepAlive) {
<< " notificationQueueSize: " << getNotificationQueueSize()
<< " nothingHandledYet(): " << nothingHandledYet();
// see if our average loop time has exceeded our limit
if ((maxLatency_ > std::chrono::microseconds::zero()) &&
(maxLatencyLoopTime_.get() > double(maxLatency_.count()))) {
maxLatencyCob_();
// back off temporarily -- don't keep spamming maxLatencyCob_
// if we're only a bit over the limit
maxLatencyLoopTime_.dampen(0.9);
if (maxLatency_ > std::chrono::microseconds::zero()) {
// see if our average loop time has exceeded our limit
if (dampenMaxLatency_ &&
(maxLatencyLoopTime_.get() > double(maxLatency_.count()))) {
maxLatencyCob_();
// back off temporarily -- don't keep spamming maxLatencyCob_
// if we're only a bit over the limit
maxLatencyLoopTime_.dampen(0.9);
} else if (!dampenMaxLatency_ && loop_time > maxLatency_) {
// If no damping, we compare the raw loop time
maxLatencyCob_();
}
}
// Our loop run did real work; reset the idle timer
......
......@@ -632,10 +632,14 @@ class EventBase : public TimeoutManager,
* called when that latency is exceeded.
* OBS: This functionality depends on time-measurement.
*/
void setMaxLatency(std::chrono::microseconds maxLatency, Func maxLatencyCob) {
void setMaxLatency(
std::chrono::microseconds maxLatency,
Func maxLatencyCob,
bool dampen = true) {
assert(enableTimeMeasurement_);
maxLatency_ = maxLatency;
maxLatencyCob_ = std::move(maxLatencyCob);
dampenMaxLatency_ = dampen;
}
/**
......@@ -917,6 +921,10 @@ class EventBase : public TimeoutManager,
// to reduce spamminess
SmoothLoopTime maxLatencyLoopTime_;
// If true, max latency callbacks will use a dampened SmoothLoopTime, else
// they'll use the raw loop time.
bool dampenMaxLatency_ = true;
// callback called when latency limit is exceeded
Func maxLatencyCob_;
......
......@@ -60,6 +60,8 @@ REGISTER_TYPED_TEST_CASE_P(
CallbackOrderTest,
AlwaysEnqueueCallbackOrderTest,
IdleTime,
MaxLatencyUndamped,
UnsetMaxLatencyUndamped,
ThisLoop,
EventBaseThreadLoop,
EventBaseThreadName,
......
......@@ -1979,6 +1979,44 @@ TYPED_TEST_P(EventBaseTest, IdleTime) {
ASSERT_EQ(21, tos->getTimeouts());
}
TYPED_TEST_P(EventBaseTest, MaxLatencyUndamped) {
auto eventBasePtr = getEventBase<TypeParam>();
folly::EventBase& eb = *eventBasePtr;
int maxDurationViolations = 0;
eb.setMaxLatency(
std::chrono::milliseconds{1}, [&]() { maxDurationViolations++; }, false);
eb.runInLoop(
[&]() {
/* sleep override */ std::this_thread::sleep_for(
std::chrono::microseconds{1001});
eb.terminateLoopSoon();
},
true);
eb.loop();
ASSERT_EQ(maxDurationViolations, 1);
}
TYPED_TEST_P(EventBaseTest, UnsetMaxLatencyUndamped) {
auto eventBasePtr = getEventBase<TypeParam>();
folly::EventBase& eb = *eventBasePtr;
int maxDurationViolations = 0;
eb.setMaxLatency(
std::chrono::milliseconds{1}, [&]() { maxDurationViolations++; }, false);
// Immediately unset it and make sure the counter isn't incremented. If the
// function gets called, this will raise an std::bad_function_call.
std::function<void()> bad_func = nullptr;
eb.setMaxLatency(std::chrono::milliseconds{0}, bad_func, false);
eb.runInLoop(
[&]() {
/* sleep override */ std::this_thread::sleep_for(
std::chrono::microseconds{1001});
eb.terminateLoopSoon();
},
true);
eb.loop();
ASSERT_EQ(maxDurationViolations, 0);
}
/**
* Test that thisLoop functionality works with terminateLoopSoon
*/
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment