Commit 09a84b28 authored by Dave Watson's avatar Dave Watson Committed by Facebook Github Bot 7

Fix scheduling bug

Summary:
Noticed this while debugging other timerwheel changes.  Scheduling new events in callbacks may result in us *running them right away* if they land in a bucket we are currently processing.

Instead, round up all the timeouts, then run them separately.  This means you can never schedule a timeout that will run immediately (0ticks), but that's probably fine.

Reviewed By: yfeldblum

Differential Revision: D3679413

fbshipit-source-id: 7e18632f23ea33c072c2718e30b378641895b094
parent 28c41287
...@@ -97,6 +97,12 @@ HHWheelTimer::~HHWheelTimer() { ...@@ -97,6 +97,12 @@ HHWheelTimer::~HHWheelTimer() {
*processingCallbacksGuard_ = true; *processingCallbacksGuard_ = true;
} }
}); });
while (!timeouts.empty()) {
auto* cb = &timeouts.front();
timeouts.pop_front();
cb->cancelTimeout();
cb->callbackCanceled();
}
cancelAll(); cancelAll();
} }
...@@ -198,17 +204,23 @@ void HHWheelTimer::timeoutExpired() noexcept { ...@@ -198,17 +204,23 @@ void HHWheelTimer::timeoutExpired() noexcept {
while (!cbs->empty()) { while (!cbs->empty()) {
auto* cb = &cbs->front(); auto* cb = &cbs->front();
cbs->pop_front(); cbs->pop_front();
count_--; timeouts.push_back(*cb);
cb->wheel_ = nullptr; }
cb->expiration_ = milliseconds(0); }
RequestContextScopeGuard rctx(cb->context_);
cb->timeoutExpired(); while (!timeouts.empty()) {
if (isDestroyed) { auto* cb = &timeouts.front();
// The HHWheelTimer itself has been destroyed. The other callbacks timeouts.pop_front();
// will have been cancelled from the destructor. Bail before causing count_--;
// damage. cb->wheel_ = nullptr;
return; cb->expiration_ = milliseconds(0);
} RequestContextScopeGuard rctx(cb->context_);
cb->timeoutExpired();
if (isDestroyed) {
// The HHWheelTimer itself has been destroyed. The other callbacks
// will have been cancelled from the destructor. Bail before causing
// damage.
return;
} }
} }
if (count_ > 0) { if (count_ > 0) {
......
...@@ -297,6 +297,7 @@ class HHWheelTimer : private folly::AsyncTimeout, ...@@ -297,6 +297,7 @@ class HHWheelTimer : private folly::AsyncTimeout,
std::chrono::milliseconds now_; std::chrono::milliseconds now_;
bool* processingCallbacksGuard_; bool* processingCallbacksGuard_;
CallbackList timeouts; // Timeouts queued to run
}; };
} // folly } // folly
...@@ -282,6 +282,63 @@ TEST_F(HHWheelTimerTest, SlowFast) { ...@@ -282,6 +282,63 @@ TEST_F(HHWheelTimerTest, SlowFast) {
T_CHECK_TIMEOUT(start, t2.timestamps[0], milliseconds(5), milliseconds(1)); T_CHECK_TIMEOUT(start, t2.timestamps[0], milliseconds(5), milliseconds(1));
} }
TEST_F(HHWheelTimerTest, ReschedTest) {
StackWheelTimer t(&eventBase, milliseconds(1));
TestTimeout t1;
TestTimeout t2;
ASSERT_EQ(t.count(), 0);
t.scheduleTimeout(&t1, milliseconds(128));
TimePoint start2;
t1.fn = [&]() {
t.scheduleTimeout(&t2, milliseconds(255)); // WHEEL_SIZE - 1
start2.reset();
ASSERT_EQ(t.count(), 1);
};
ASSERT_EQ(t.count(), 1);
TimePoint start;
eventBase.loop();
TimePoint end;
ASSERT_EQ(t1.timestamps.size(), 1);
ASSERT_EQ(t2.timestamps.size(), 1);
ASSERT_EQ(t.count(), 0);
T_CHECK_TIMEOUT(start, t1.timestamps[0], milliseconds(128), milliseconds(1));
T_CHECK_TIMEOUT(start2, t2.timestamps[0], milliseconds(255), milliseconds(1));
}
TEST_F(HHWheelTimerTest, DeleteWheelInTimeout) {
auto t = HHWheelTimer::newTimer(&eventBase, milliseconds(1));
TestTimeout t1;
TestTimeout t2;
TestTimeout t3;
ASSERT_EQ(t->count(), 0);
t->scheduleTimeout(&t1, milliseconds(128));
t->scheduleTimeout(&t2, milliseconds(128));
t->scheduleTimeout(&t3, milliseconds(128));
t1.fn = [&]() { t2.cancelTimeout(); };
t3.fn = [&]() { t.reset(); };
ASSERT_EQ(t->count(), 3);
TimePoint start;
eventBase.loop();
TimePoint end;
ASSERT_EQ(t1.timestamps.size(), 1);
ASSERT_EQ(t2.timestamps.size(), 0);
T_CHECK_TIMEOUT(start, t1.timestamps[0], milliseconds(128), milliseconds(1));
}
/* /*
* Test scheduling a mix of timers with default timeout and variable timeout. * Test scheduling a mix of timers with default timeout and variable timeout.
*/ */
......
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