Commit 5359a760 authored by Andrii Grynenko's avatar Andrii Grynenko Committed by Facebook Github Bot

Simplify EventBaseLoopController

Summary: Make EventBaseLoopController not dependent on EventBase's runOnDestruction. This will allow us to make FiberManager work with any Executor.

Reviewed By: yfeldblum

Differential Revision: D6828478

fbshipit-source-id: 4d74776044a02954d022ef6f6f1e6834839973b8
parent 425c8833
......@@ -96,6 +96,21 @@ class AtomicIntrusiveLinkedList {
return oldHead == nullptr;
}
/**
* Replaces the head with nullptr,
* and calls func() on the removed elements in the order from tail to head.
* Returns false if the list was empty.
*/
template <typename F>
bool sweepOnce(F&& func) {
if (auto head = head_.exchange(nullptr)) {
auto rhead = reverse(head);
unlinkAll(rhead, std::forward<F>(func));
return true;
}
return false;
}
/**
* Repeatedly replaces the head with nullptr,
* and calls func() on the removed elements in the order from tail to head.
......@@ -103,9 +118,7 @@ class AtomicIntrusiveLinkedList {
*/
template <typename F>
void sweep(F&& func) {
while (auto head = head_.exchange(nullptr)) {
auto rhead = reverse(head);
unlinkAll(rhead, std::forward<F>(func));
while (sweepOnce(func)) {
}
}
......
......@@ -19,8 +19,7 @@
namespace folly {
namespace fibers {
inline EventBaseLoopController::EventBaseLoopController()
: callback_(*this), aliveWeak_(destructionCallback_.getWeak()) {}
inline EventBaseLoopController::EventBaseLoopController() : callback_(*this) {}
inline EventBaseLoopController::~EventBaseLoopController() {
callback_.cancelLoopCallback();
......@@ -38,8 +37,6 @@ inline void EventBaseLoopController::attachEventBase(
}
eventBase_ = &eventBase;
eventBase_->runOnDestruction(&destructionCallback_);
eventBaseAttached_ = true;
if (awaitingScheduling_) {
......@@ -66,10 +63,6 @@ inline void EventBaseLoopController::schedule() {
}
}
inline void EventBaseLoopController::cancel() {
callback_.cancelLoopCallback();
}
inline void EventBaseLoopController::runLoop() {
if (!eventBaseKeepAlive_) {
// runLoop can be called twice if both schedule() and scheduleThreadSafe()
......@@ -80,7 +73,9 @@ inline void EventBaseLoopController::runLoop() {
eventBaseKeepAlive_ = eventBase_->getKeepAliveToken();
}
if (loopRunner_) {
if (fm_->hasReadyTasks()) {
loopRunner_->run([&] { fm_->loopUntilNoReadyImpl(); });
}
} else {
fm_->loopUntilNoReadyImpl();
}
......@@ -98,13 +93,14 @@ inline void EventBaseLoopController::scheduleThreadSafe(
3) We fulfill the promise from the other thread. */
assert(eventBaseAttached_);
auto alive = aliveWeak_.lock();
if (func()) {
eventBase_->runInEventBaseThread([this]() {
if (fm_->shouldRunLoopRemote()) {
return runLoop();
}
if (func() && alive) {
auto aliveWeak = aliveWeak_;
eventBase_->runInEventBaseThread([this, aliveWeak]() {
if (!aliveWeak.expired()) {
runLoop();
if (!fm_->hasTasks()) {
eventBaseKeepAlive_.reset();
}
});
}
......
......@@ -57,50 +57,18 @@ class EventBaseLoopController : public LoopController {
EventBaseLoopController& controller_;
};
class DestructionCallback : public folly::EventBase::LoopCallback {
public:
DestructionCallback() : alive_(new int(42)) {}
~DestructionCallback() override {
reset();
}
void runLoopCallback() noexcept override {
reset();
}
std::weak_ptr<void> getWeak() {
return {alive_};
}
private:
void reset() {
std::weak_ptr<void> aliveWeak(alive_);
alive_.reset();
while (!aliveWeak.expired()) {
// Spin until all operations requiring EventBaseLoopController to be
// alive are complete.
}
}
std::shared_ptr<void> alive_;
};
bool awaitingScheduling_{false};
VirtualEventBase* eventBase_{nullptr};
Executor::KeepAlive eventBaseKeepAlive_;
ControllerCallback callback_;
DestructionCallback destructionCallback_;
FiberManager* fm_{nullptr};
std::atomic<bool> eventBaseAttached_{false};
std::weak_ptr<void> aliveWeak_;
InlineFunctionRunner* loopRunner_{nullptr};
/* LoopController interface */
void setFiberManager(FiberManager* fm) override;
void schedule() override;
void cancel() override;
void runLoop() override;
void scheduleThreadSafe(std::function<bool()> func) override;
void timedSchedule(std::function<void()> func, TimePoint time) override;
......
......@@ -79,9 +79,7 @@ FiberManager::FiberManager(
std::move(options)) {}
FiberManager::~FiberManager() {
if (isLoopScheduled_) {
loopController_->cancel();
}
loopController_.reset();
while (!fibersPool_.empty()) {
fibersPool_.pop_front_and_dispose([](Fiber* fiber) { delete fiber; });
......@@ -100,7 +98,7 @@ const LoopController& FiberManager::loopController() const {
bool FiberManager::hasTasks() const {
return fibersActive_ > 0 || !remoteReadyQueue_.empty() ||
!remoteTaskQueue_.empty();
!remoteTaskQueue_.empty() || remoteCount_ > 0;
}
Fiber* FiberManager::getFiber() {
......
......@@ -209,22 +209,23 @@ inline void FiberManager::loopUntilNoReadyImpl() {
CHECK_EQ(this, originalFiberManager);
};
bool hadRemoteFiber = true;
while (hadRemoteFiber) {
hadRemoteFiber = false;
bool hadRemote = true;
while (hadRemote) {
while (!readyFibers_.empty()) {
auto& fiber = readyFibers_.front();
readyFibers_.pop_front();
runReadyFiber(&fiber);
}
remoteReadyQueue_.sweep([this, &hadRemoteFiber](Fiber* fiber) {
runReadyFiber(fiber);
hadRemoteFiber = true;
});
auto hadRemoteFiber = remoteReadyQueue_.sweepOnce(
[this](Fiber* fiber) { runReadyFiber(fiber); });
remoteTaskQueue_.sweep([this, &hadRemoteFiber](RemoteTask* taskPtr) {
if (hadRemoteFiber) {
++remoteCount_;
}
auto hadRemoteTask =
remoteTaskQueue_.sweepOnce([this](RemoteTask* taskPtr) {
std::unique_ptr<RemoteTask> task(taskPtr);
auto fiber = getFiber();
if (task->localData) {
......@@ -237,8 +238,13 @@ inline void FiberManager::loopUntilNoReadyImpl() {
observer_->runnable(reinterpret_cast<uintptr_t>(fiber));
}
runReadyFiber(fiber);
hadRemoteFiber = true;
});
if (hadRemoteTask) {
++remoteCount_;
}
hadRemote = hadRemoteTask || hadRemoteFiber;
}
if (observer_) {
......@@ -249,6 +255,16 @@ inline void FiberManager::loopUntilNoReadyImpl() {
readyFibers_.splice(readyFibers_.end(), yieldedFibers_);
}
inline bool FiberManager::shouldRunLoopRemote() {
--remoteCount_;
return !remoteReadyQueue_.empty() || !remoteTaskQueue_.empty();
}
inline bool FiberManager::hasReadyTasks() const {
return !readyFibers_.empty() || !remoteReadyQueue_.empty() ||
!remoteTaskQueue_.empty();
}
// We need this to be in a struct, not inlined in addTask, because clang crashes
// otherwise.
template <typename F>
......
......@@ -164,11 +164,21 @@ class FiberManager : public ::folly::Executor {
*/
void loopUntilNoReadyImpl();
/**
* This should only be called by a LoopController.
*/
bool shouldRunLoopRemote();
/**
* @return true if there are outstanding tasks.
*/
bool hasTasks() const;
/**
* @return true if there are tasks ready to run.
*/
bool hasReadyTasks() const;
/**
* Sets exception callback which will be called if any of the tasks throws an
* exception.
......@@ -442,6 +452,8 @@ class FiberManager : public ::folly::Executor {
folly::AtomicIntrusiveLinkedList<RemoteTask, &RemoteTask::nextRemoteTask>
remoteTaskQueue_;
ssize_t remoteCount_{0};
std::shared_ptr<TimeoutController> timeoutManager_;
struct FibersPoolResizer {
......
......@@ -37,7 +37,7 @@ class LoopController {
/**
* Called by FiberManager to schedule the loop function run
* at some point in the future.
* at some point in the futufre.
*/
virtual void schedule() = 0;
......@@ -53,12 +53,6 @@ class LoopController {
*/
virtual void scheduleThreadSafe(std::function<bool()> func) = 0;
/**
* Called by FiberManager to cancel a previously scheduled
* loop function run.
*/
virtual void cancel() = 0;
/**
* Called by FiberManager to schedule some function to be run at some time.
*/
......
......@@ -28,6 +28,10 @@ class SimpleLoopController : public LoopController {
public:
SimpleLoopController() : fm_(nullptr), stopRequested_(false) {}
~SimpleLoopController() {
scheduled_ = false;
}
/**
* Run FiberManager loop; if no ready task are present,
* run provided function. Stops after both stop() has been called
......@@ -72,8 +76,18 @@ class SimpleLoopController : public LoopController {
}
void runLoop() override {
do {
if (remoteLoopRun_ < remoteScheduleCalled_) {
for (; remoteLoopRun_ < remoteScheduleCalled_; ++remoteLoopRun_) {
if (fm_->shouldRunLoopRemote()) {
fm_->loopUntilNoReadyImpl();
}
}
} else {
fm_->loopUntilNoReadyImpl();
}
} while (remoteLoopRun_ < remoteScheduleCalled_);
}
void schedule() override {
scheduled_ = true;
......@@ -88,6 +102,7 @@ class SimpleLoopController : public LoopController {
std::atomic<bool> scheduled_{false};
bool stopRequested_;
std::atomic<int> remoteScheduleCalled_{0};
int remoteLoopRun_{0};
std::vector<std::pair<TimePoint, std::function<void()>>> scheduledFuncs_;
/* LoopController interface */
......@@ -96,10 +111,6 @@ class SimpleLoopController : public LoopController {
fm_ = fm;
}
void cancel() override {
scheduled_ = false;
}
void scheduleThreadSafe(std::function<bool()> func) override {
if (func()) {
++remoteScheduleCalled_;
......
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