Commit 4479aa0c authored by Andrii Grynenko's avatar Andrii Grynenko Committed by Facebook GitHub Bot

Fix pool resizer to correctly handle VirtualEventBase destruction

Summary: It's possible that we still have a recurring pool resizer task scheduled after virtual event base is destroyed. This changes makes sure that we don't try to reschedule it if VirtualEventBase is already destroyed.

Reviewed By: yfeldblum

Differential Revision: D21875241

fbshipit-source-id: c4622c5d103c6291386055c153ab4aeebe7795a2
parent 3ee93a0f
...@@ -134,7 +134,7 @@ bool Baton::try_wait_until( ...@@ -134,7 +134,7 @@ bool Baton::try_wait_until(
const auto now = Clock::now(); const auto now = Clock::now();
const auto timeoutMs = std::chrono::duration_cast<std::chrono::milliseconds>( const auto timeoutMs = std::chrono::duration_cast<std::chrono::milliseconds>(
FOLLY_LIKELY(now <= deadline) ? deadline - now : Duration{}); FOLLY_LIKELY(now <= deadline) ? deadline - now : Duration{});
fm->loopController_->timer().scheduleTimeout(&handler, timeoutMs); fm->loopController_->timer()->scheduleTimeout(&handler, timeoutMs);
waitFiber(*fm, static_cast<F&&>(mainContextFunc)); waitFiber(*fm, static_cast<F&&>(mainContextFunc));
return waiter_ == POSTED; return waiter_ == POSTED;
......
...@@ -132,7 +132,7 @@ void Baton::TimeoutHandler::scheduleTimeout(std::chrono::milliseconds timeout) { ...@@ -132,7 +132,7 @@ void Baton::TimeoutHandler::scheduleTimeout(std::chrono::milliseconds timeout) {
assert(timeoutFunc_ != nullptr); assert(timeoutFunc_ != nullptr);
if (timeout.count() > 0) { if (timeout.count() > 0) {
fiberManager_->loopController_->timer().scheduleTimeout(this, timeout); fiberManager_->loopController_->timer()->scheduleTimeout(this, timeout);
} }
} }
......
...@@ -33,12 +33,17 @@ inline void EventBaseLoopController::attachEventBase(EventBase& eventBase) { ...@@ -33,12 +33,17 @@ inline void EventBaseLoopController::attachEventBase(EventBase& eventBase) {
inline void EventBaseLoopController::attachEventBase( inline void EventBaseLoopController::attachEventBase(
VirtualEventBase& eventBase) { VirtualEventBase& eventBase) {
if (eventBase_ != nullptr) { if (eventBaseAttached_.exchange(true)) {
LOG(ERROR) << "Attempt to reattach EventBase to LoopController"; LOG(ERROR) << "Attempt to reattach EventBase to LoopController";
return;
} }
eventBase_ = &eventBase; eventBase_ = &eventBase;
eventBaseAttached_ = true;
CancellationSource source;
eventBaseShutdownToken_ = source.getToken();
eventBase_->runOnDestruction(
[source = std::move(source)] { source.requestCancellation(); });
if (awaitingScheduling_) { if (awaitingScheduling_) {
schedule(); schedule();
...@@ -119,10 +124,14 @@ inline void EventBaseLoopController::scheduleThreadSafe() { ...@@ -119,10 +124,14 @@ inline void EventBaseLoopController::scheduleThreadSafe() {
}); });
} }
inline HHWheelTimer& EventBaseLoopController::timer() { inline HHWheelTimer* EventBaseLoopController::timer() {
assert(eventBaseAttached_); assert(eventBaseAttached_);
return eventBase_->timer(); if (UNLIKELY(eventBaseShutdownToken_.isCancellationRequested())) {
return nullptr;
}
return &eventBase_->timer();
} }
} // namespace fibers } // namespace fibers
} // namespace folly } // namespace folly
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#pragma once #pragma once
#include <folly/CancellationToken.h>
#include <folly/fibers/ExecutorBasedLoopController.h> #include <folly/fibers/ExecutorBasedLoopController.h>
#include <folly/fibers/FiberManagerInternal.h> #include <folly/fibers/FiberManagerInternal.h>
#include <folly/io/async/VirtualEventBase.h> #include <folly/io/async/VirtualEventBase.h>
...@@ -62,6 +63,8 @@ class EventBaseLoopController : public ExecutorBasedLoopController { ...@@ -62,6 +63,8 @@ class EventBaseLoopController : public ExecutorBasedLoopController {
EventBaseLoopController& controller_; EventBaseLoopController& controller_;
}; };
folly::CancellationToken eventBaseShutdownToken_;
bool awaitingScheduling_{false}; bool awaitingScheduling_{false};
VirtualEventBase* eventBase_{nullptr}; VirtualEventBase* eventBase_{nullptr};
Executor::KeepAlive<VirtualEventBase> eventBaseKeepAlive_; Executor::KeepAlive<VirtualEventBase> eventBaseKeepAlive_;
...@@ -77,7 +80,7 @@ class EventBaseLoopController : public ExecutorBasedLoopController { ...@@ -77,7 +80,7 @@ class EventBaseLoopController : public ExecutorBasedLoopController {
void runLoop() override; void runLoop() override;
void runEagerFiber(Fiber*) override; void runEagerFiber(Fiber*) override;
void scheduleThreadSafe() override; void scheduleThreadSafe() override;
HHWheelTimer& timer() override; HHWheelTimer* timer() override;
friend class FiberManager; friend class FiberManager;
}; };
......
...@@ -71,8 +71,8 @@ inline void ExecutorLoopController::scheduleThreadSafe() { ...@@ -71,8 +71,8 @@ inline void ExecutorLoopController::scheduleThreadSafe() {
}); });
} }
inline HHWheelTimer& ExecutorLoopController::timer() { inline HHWheelTimer* ExecutorLoopController::timer() {
return *timer_; return timer_.get();
} }
} // namespace fibers } // namespace fibers
......
...@@ -94,7 +94,7 @@ class ExecutorLoopController : public fibers::ExecutorBasedLoopController { ...@@ -94,7 +94,7 @@ class ExecutorLoopController : public fibers::ExecutorBasedLoopController {
void runLoop() override; void runLoop() override;
void runEagerFiber(Fiber*) override; void runEagerFiber(Fiber*) override;
void scheduleThreadSafe() override; void scheduleThreadSafe() override;
HHWheelTimer& timer() override; HHWheelTimer* timer() override;
friend class fibers::FiberManager; friend class fibers::FiberManager;
}; };
......
...@@ -199,10 +199,12 @@ void FiberManager::doFibersPoolResizing() { ...@@ -199,10 +199,12 @@ void FiberManager::doFibersPoolResizing() {
void FiberManager::FibersPoolResizer::run() { void FiberManager::FibersPoolResizer::run() {
fiberManager_.doFibersPoolResizing(); fiberManager_.doFibersPoolResizing();
fiberManager_.loopController_->timer().scheduleTimeout( if (auto timer = fiberManager_.loopController_->timer()) {
this, timer->scheduleTimeout(
std::chrono::milliseconds( this,
fiberManager_.options_.fibersPoolResizePeriodMs)); std::chrono::milliseconds(
fiberManager_.options_.fibersPoolResizePeriodMs));
}
} }
#ifdef FOLLY_SANITIZE_ADDRESS #ifdef FOLLY_SANITIZE_ADDRESS
......
...@@ -64,8 +64,10 @@ class LoopController { ...@@ -64,8 +64,10 @@ class LoopController {
/** /**
* Used by FiberManager to schedule some function to be run at some time. * Used by FiberManager to schedule some function to be run at some time.
* May return null, but only if called outside of runLoop() call (e.g. if
* Executor backing the timer is already destroyed).
*/ */
virtual HHWheelTimer& timer() = 0; virtual HHWheelTimer* timer() = 0;
}; };
} // namespace fibers } // namespace fibers
} // namespace folly } // namespace folly
...@@ -92,8 +92,8 @@ class SimpleLoopController : public LoopController { ...@@ -92,8 +92,8 @@ class SimpleLoopController : public LoopController {
scheduled_ = true; scheduled_ = true;
} }
HHWheelTimer& timer() override { HHWheelTimer* timer() override {
return *timer_; return timer_.get();
} }
bool isInLoopThread() const { bool isInLoopThread() const {
......
...@@ -1478,6 +1478,10 @@ TEST(FiberManager, resizePeriodically) { ...@@ -1478,6 +1478,10 @@ TEST(FiberManager, resizePeriodically) {
evb.loopOnce(); evb.loopOnce();
EXPECT_EQ(5, manager.fibersAllocated()); EXPECT_EQ(5, manager.fibersAllocated());
EXPECT_EQ(5, manager.fibersPoolSize()); EXPECT_EQ(5, manager.fibersPoolSize());
// Sleep again before destruction to force the case where the
// resize timer fires during destruction of the EventBase.
std::this_thread::sleep_for(std::chrono::milliseconds(400));
} }
TEST(FiberManager, batonWaitTimeoutHandler) { TEST(FiberManager, batonWaitTimeoutHandler) {
......
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