Commit da13f8bc authored by Yedidya Feldblum's avatar Yedidya Feldblum Committed by Facebook GitHub Bot

EventBase::getRecentSteadyTime

Summary:
[Folly] `EventBase::getRecentSteadyTime`, returning a recent `steady_clock::time_point` which is updated each pass through the event loop, but not for each ready event or function.

Assuming everything in the loop is truly asynchronous, this permits callers to get a reasonably accurate approximation of the current time, but for many callers to blend or amortize the cost of getting that current time.

May only be called from the running IO thread.

Differential Revision: D21866783

fbshipit-source-id: 8eb472ce50bf012616e567b29f6724c67ef8d41a
parent 33403f55
...@@ -215,6 +215,8 @@ EventBase::~EventBase() { ...@@ -215,6 +215,8 @@ EventBase::~EventBase() {
virtualEventBaseDestroyFuture.get(); virtualEventBaseDestroyFuture.get();
} }
bumpHandlingTime();
// Call all destruction callbacks, before we start cleaning up our state. // Call all destruction callbacks, before we start cleaning up our state.
while (!onDestructionCallbacks_.rlock()->empty()) { while (!onDestructionCallbacks_.rlock()->empty()) {
OnDestructionCallback::List callbacks; OnDestructionCallback::List callbacks;
...@@ -381,6 +383,8 @@ bool EventBase::loopBody(int flags, bool ignoreKeepAlive) { ...@@ -381,6 +383,8 @@ bool EventBase::loopBody(int flags, bool ignoreKeepAlive) {
} }
++nextLoopCnt_; ++nextLoopCnt_;
bumpHandlingTime();
// Run the before loop callbacks // Run the before loop callbacks
LoopCallbackList callbacks; LoopCallbackList callbacks;
callbacks.swap(runBeforeLoopCallbacks_); callbacks.swap(runBeforeLoopCallbacks_);
...@@ -402,11 +406,12 @@ bool EventBase::loopBody(int flags, bool ignoreKeepAlive) { ...@@ -402,11 +406,12 @@ bool EventBase::loopBody(int flags, bool ignoreKeepAlive) {
ranLoopCallbacks = runLoopCallbacks(); ranLoopCallbacks = runLoopCallbacks();
if (enableTimeMeasurement_) { if (enableTimeMeasurement_) {
assert(startWork_);
auto now = std::chrono::steady_clock::now(); auto now = std::chrono::steady_clock::now();
busy = std::chrono::duration_cast<std::chrono::microseconds>( busy = std::chrono::duration_cast<std::chrono::microseconds>(
now - startWork_); now - *startWork_);
idle = std::chrono::duration_cast<std::chrono::microseconds>( idle = std::chrono::duration_cast<std::chrono::microseconds>(
startWork_ - idleStart); *startWork_ - idleStart);
auto loop_time = busy + idle; auto loop_time = busy + idle;
avgLoopTime_.addSample(loop_time, busy); avgLoopTime_.addSample(loop_time, busy);
...@@ -556,7 +561,7 @@ void EventBase::bumpHandlingTime() { ...@@ -556,7 +561,7 @@ void EventBase::bumpHandlingTime() {
startWork_ = std::chrono::steady_clock::now(); startWork_ = std::chrono::steady_clock::now();
VLOG(11) << "EventBase " << this << " " << __PRETTY_FUNCTION__ VLOG(11) << "EventBase " << this << " " << __PRETTY_FUNCTION__
<< " (loop) startWork_ " << startWork_.time_since_epoch().count(); << " (loop) startWork_ " << startWork_->time_since_epoch().count();
} }
} }
...@@ -682,7 +687,6 @@ void EventBase::runImmediatelyOrRunInEventBaseThreadAndWait(Func fn) noexcept { ...@@ -682,7 +687,6 @@ void EventBase::runImmediatelyOrRunInEventBaseThreadAndWait(Func fn) noexcept {
} }
bool EventBase::runLoopCallbacks() { bool EventBase::runLoopCallbacks() {
bumpHandlingTime();
if (!loopCallbacks_.empty()) { if (!loopCallbacks_.empty()) {
// Swap the loopCallbacks_ list with a temporary list on our stack. // Swap the loopCallbacks_ list with a temporary list on our stack.
// This way we will only run callbacks scheduled at the time // This way we will only run callbacks scheduled at the time
......
...@@ -781,6 +781,12 @@ class EventBase : public TimeoutManager, ...@@ -781,6 +781,12 @@ class EventBase : public TimeoutManager,
loopOnce(); loopOnce();
} }
Optional<std::chrono::steady_clock::time_point> getRecentSteadyTime() const
noexcept {
dcheckIsInEventBaseThread();
return startWork_;
}
// Implements the ScheduledExecutor interface // Implements the ScheduledExecutor interface
void scheduleAt(Func&& fn, TimePoint const& timeout) override; void scheduleAt(Func&& fn, TimePoint const& timeout) override;
...@@ -908,7 +914,7 @@ class EventBase : public TimeoutManager, ...@@ -908,7 +914,7 @@ class EventBase : public TimeoutManager,
// Wrap-around loop counter to detect beginning of each loop // Wrap-around loop counter to detect beginning of each loop
std::size_t nextLoopCnt_; std::size_t nextLoopCnt_;
std::size_t latestLoopCnt_; std::size_t latestLoopCnt_;
std::chrono::steady_clock::time_point startWork_; Optional<std::chrono::steady_clock::time_point> startWork_;
// Prevent undefined behavior from invoking event_base_loop() reentrantly. // Prevent undefined behavior from invoking event_base_loop() reentrantly.
// This is needed since many projects use libevent-1.4, which lacks commit // This is needed since many projects use libevent-1.4, which lacks commit
// b557b175c00dc462c1fce25f6e7dd67121d2c001 from // b557b175c00dc462c1fce25f6e7dd67121d2c001 from
......
...@@ -2286,6 +2286,27 @@ TYPED_TEST_P(EventBaseTest1, RunOnDestructionAddCallbackWithinCallback) { ...@@ -2286,6 +2286,27 @@ TYPED_TEST_P(EventBaseTest1, RunOnDestructionAddCallbackWithinCallback) {
EXPECT_EQ(2, callbacksCalled); EXPECT_EQ(2, callbacksCalled);
} }
TYPED_TEST_P(EventBaseTest1, RecentSteadyTime) {
using namespace std::literals::chrono_literals;
using tp = std::chrono::steady_clock::time_point;
FOLLY_SKIP_IF_NULLPTR_BACKEND(evb);
tp a;
tp b;
tp c;
evb.runInEventBaseThread([&] {
a = evb.getRecentSteadyTime().value();
/* sleep override */ std::this_thread::sleep_for(10ms);
b = evb.getRecentSteadyTime().value();
});
evb.loop();
evb.runInEventBaseThread([&] { //
c = evb.getRecentSteadyTime().value();
});
evb.loop();
EXPECT_EQ(a, b);
EXPECT_LT(a, c);
}
REGISTER_TYPED_TEST_CASE_P( REGISTER_TYPED_TEST_CASE_P(
EventBaseTest, EventBaseTest,
ReadEvent, ReadEvent,
...@@ -2350,6 +2371,7 @@ REGISTER_TYPED_TEST_CASE_P( ...@@ -2350,6 +2371,7 @@ REGISTER_TYPED_TEST_CASE_P(
RunOnDestructionCancelled, RunOnDestructionCancelled,
RunOnDestructionAfterHandleDestroyed, RunOnDestructionAfterHandleDestroyed,
RunOnDestructionAddCallbackWithinCallback, RunOnDestructionAddCallbackWithinCallback,
InternalExternalCallbackOrderTest); InternalExternalCallbackOrderTest,
RecentSteadyTime);
} // namespace test } // namespace test
} // namespace folly } // namespace folly
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