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

Simplify DeferredExecutor

Summary: Move the wait() and timed_wait() functionality out of DeferredExecutor. This is now possible because folly::Futures are using Executor::KeepAlive to manage executors' life-time.

Reviewed By: LeeHowes

Differential Revision: D9925417

fbshipit-source-id: e9818417048f44ee66648439e781060fdabca6b9
parent e3f27085
...@@ -500,194 +500,194 @@ class DeferredExecutor final : public Executor { ...@@ -500,194 +500,194 @@ class DeferredExecutor final : public Executor {
public: public:
void add(Func func) override { void add(Func func) override {
auto state = state_.load(std::memory_order_acquire); auto state = state_.load(std::memory_order_acquire);
if (state == State::HAS_FUNCTION) { if (state == State::DETACHED) {
// This means we are inside runAndDestroy, just run the function inline
func();
return; return;
} }
func_ = std::move(func);
std::shared_ptr<FutureBatonType> baton;
do {
if (state == State::HAS_EXECUTOR) { if (state == State::HAS_EXECUTOR) {
state_.store(State::HAS_FUNCTION, std::memory_order_release); executor_->add(std::move(func));
executor_->add([this] { this->runAndDestroy(); });
return; return;
} }
if (state == State::DETACHED) { DCHECK(state == State::EMPTY);
// Function destructor may trigger more functions to be added to the func_ = std::move(func);
// Executor. They should be run inline. if (state_.compare_exchange_strong(
state = State::HAS_FUNCTION;
func_ = nullptr;
delete this;
return;
}
if (state == State::HAS_BATON) {
baton = baton_.copy();
}
assert(state == State::EMPTY || state == State::HAS_BATON);
} while (!state_.compare_exchange_weak(
state, state,
State::HAS_FUNCTION, State::HAS_FUNCTION,
std::memory_order_release, std::memory_order_release,
std::memory_order_acquire)); std::memory_order_acquire)) {
return;
// After compare_exchange_weak is complete, we can no longer use this }
// object since it may be destroyed from another thread. DCHECK(state == State::DETACHED || state == State::HAS_EXECUTOR);
if (baton) { if (state == State::DETACHED) {
baton->post(); std::exchange(func_, nullptr);
return;
} }
executor_->add(std::exchange(func_, nullptr));
} }
void setExecutor(folly::Executor* executor) { void setExecutor(folly::Executor::KeepAlive<> executor) {
DCHECK(!dynamic_cast<DeferredExecutor*>(executor)); DCHECK(!dynamic_cast<DeferredExecutor*>(executor.get()));
if (nestedExecutors_) { if (nestedExecutors_) {
for (auto nestedExecutor : *nestedExecutors_) { auto nestedExecutors = std::exchange(nestedExecutors_, nullptr);
nestedExecutor->setExecutor(executor); for (auto& nestedExecutor : *nestedExecutors) {
nestedExecutor->setExecutor(executor.copy());
} }
} }
executor_ = executor; executor_ = std::move(executor);
auto state = state_.load(std::memory_order_acquire); auto state = state_.load(std::memory_order_acquire);
do { if (state == State::EMPTY &&
if (state == State::HAS_FUNCTION) { state_.compare_exchange_strong(
executor_->add([this] { this->runAndDestroy(); });
return;
}
assert(state == State::EMPTY);
} while (!state_.compare_exchange_weak(
state, state,
State::HAS_EXECUTOR, State::HAS_EXECUTOR,
std::memory_order_release, std::memory_order_release,
std::memory_order_acquire)); std::memory_order_acquire)) {
return;
} }
void runAndDestroy() { DCHECK(state == State::HAS_FUNCTION);
assert(state_.load(std::memory_order_relaxed) == State::HAS_FUNCTION); state_.store(State::HAS_EXECUTOR, std::memory_order_release);
func_(); executor_->add(std::exchange(func_, nullptr));
delete this;
} }
void detach() { void detach() {
if (nestedExecutors_) { if (nestedExecutors_) {
for (auto nestedExecutor : *nestedExecutors_) { auto nestedExecutors = std::exchange(nestedExecutors_, nullptr);
for (auto& nestedExecutor : *nestedExecutors) {
nestedExecutor->detach(); nestedExecutor->detach();
} }
} }
auto state = state_.load(std::memory_order_acquire); auto state = state_.load(std::memory_order_acquire);
do { if (state == State::EMPTY &&
if (state == State::HAS_FUNCTION) { state_.compare_exchange_strong(
// Function destructor may trigger more functions to be added to the
// Executor. They should be run inline.
func_ = nullptr;
delete this;
return;
}
assert(state == State::EMPTY);
} while (!state_.compare_exchange_weak(
state, state,
State::DETACHED, State::DETACHED,
std::memory_order_release, std::memory_order_release,
std::memory_order_acquire)); std::memory_order_acquire)) {
return;
} }
void wait() { DCHECK(state == State::HAS_FUNCTION);
if (nestedExecutors_) { state_.store(State::DETACHED, std::memory_order_release);
for (auto nestedExecutor : *nestedExecutors_) { std::exchange(func_, nullptr);
nestedExecutor->wait();
nestedExecutor->runAndDestroy();
} }
return;
void setNestedExecutors(
std::vector<folly::Executor::KeepAlive<DeferredExecutor>> executors) {
DCHECK(!nestedExecutors_);
nestedExecutors_ = std::make_unique<
std::vector<folly::Executor::KeepAlive<DeferredExecutor>>>(
std::move(executors));
} }
auto state = state_.load(std::memory_order_acquire);
auto baton = std::make_shared<FutureBatonType>(); static KeepAlive<DeferredExecutor> create() {
baton_ = baton; return makeKeepAlive<DeferredExecutor>(new DeferredExecutor());
do {
if (state == State::HAS_FUNCTION) {
return;
} }
assert(state == State::EMPTY);
} while (!state_.compare_exchange_weak(
state,
State::HAS_BATON,
std::memory_order_release,
std::memory_order_acquire));
baton->wait(); private:
DeferredExecutor() {}
assert(state_.load(std::memory_order_relaxed) == State::HAS_FUNCTION); bool keepAliveAcquire() override {
auto keepAliveCount =
keepAliveCount_.fetch_add(1, std::memory_order_relaxed);
DCHECK(keepAliveCount > 0);
return true;
} }
using Clock = std::chrono::steady_clock; void keepAliveRelease() override {
auto keepAliveCount =
bool wait(Duration duration) { keepAliveCount_.fetch_sub(1, std::memory_order_acq_rel);
return wait_until(Clock::now() + duration); DCHECK(keepAliveCount > 0);
if (keepAliveCount == 1) {
delete this;
}
} }
bool wait_until(Clock::time_point deadline) { enum class State { EMPTY, HAS_FUNCTION, HAS_EXECUTOR, DETACHED };
if (nestedExecutors_) { std::atomic<State> state_{State::EMPTY};
for (auto nestedExecutor : *nestedExecutors_) { Func func_;
if (!nestedExecutor->wait_until(deadline)) { folly::Executor::KeepAlive<> executor_;
return false; std::unique_ptr<std::vector<folly::Executor::KeepAlive<DeferredExecutor>>>
nestedExecutors_;
std::atomic<ssize_t> keepAliveCount_{1};
};
class WaitExecutor final : public folly::Executor {
public:
void add(Func func) override {
auto wQueue = queue_.wlock();
if (wQueue->detached) {
return;
}
bool empty = wQueue->funcs.empty();
wQueue->funcs.push_back(std::move(func));
if (empty) {
baton_.post();
} }
} }
for (auto nestedExecutor : *nestedExecutors_) {
nestedExecutor->runAndDestroy(); void drive() {
baton_.wait();
baton_.reset();
auto funcs = std::move(queue_.wlock()->funcs);
for (auto& func : funcs) {
std::exchange(func, nullptr)();
} }
} }
auto state = state_.load(std::memory_order_acquire); using Clock = std::chrono::steady_clock;
auto baton = std::make_shared<FutureBatonType>();
baton_ = baton; bool driveUntil(Clock::time_point deadline) {
do { if (!baton_.try_wait_until(deadline)) {
if (state == State::HAS_FUNCTION) { return false;
}
baton_.reset();
auto funcs = std::move(queue_.wlock()->funcs);
for (auto& func : funcs) {
std::exchange(func, nullptr)();
}
return true; return true;
} }
assert(state == State::EMPTY);
} while (!state_.compare_exchange_weak(
state,
State::HAS_BATON,
std::memory_order_release,
std::memory_order_acquire));
if (baton->try_wait_until(deadline)) { void detach() {
assert(state_.load(std::memory_order_relaxed) == State::HAS_FUNCTION); // Make sure we don't hold the lock while destroying funcs.
return true; [&] {
auto wQueue = queue_.wlock();
wQueue->detached = true;
return std::move(wQueue->funcs);
}();
} }
state = state_.load(std::memory_order_acquire); static KeepAlive<WaitExecutor> create() {
do { return makeKeepAlive<WaitExecutor>(new WaitExecutor());
if (state == State::HAS_FUNCTION) {
return true;
} }
assert(state == State::HAS_BATON);
} while (!state_.compare_exchange_weak( private:
state, WaitExecutor() {}
State::EMPTY,
std::memory_order_release, bool keepAliveAcquire() override {
std::memory_order_acquire)); auto keepAliveCount =
return false; keepAliveCount_.fetch_add(1, std::memory_order_relaxed);
DCHECK(keepAliveCount > 0);
return true;
} }
void setNestedExecutors(std::vector<DeferredExecutor*> executors) { void keepAliveRelease() override {
DCHECK(!nestedExecutors_); auto keepAliveCount =
nestedExecutors_ = keepAliveCount_.fetch_sub(1, std::memory_order_acq_rel);
std::make_unique<std::vector<DeferredExecutor*>>(executors); DCHECK(keepAliveCount > 0);
if (keepAliveCount == 1) {
delete this;
}
} }
private: struct Queue {
enum class State { std::vector<Func> funcs;
EMPTY, bool detached{false};
HAS_FUNCTION,
HAS_EXECUTOR,
HAS_BATON,
DETACHED,
}; };
std::atomic<State> state_{State::EMPTY};
Func func_; folly::Synchronized<Queue> queue_;
Executor* executor_; FutureBatonType baton_;
folly::Synchronized<std::shared_ptr<FutureBatonType>> baton_;
std::unique_ptr<std::vector<DeferredExecutor*>> nestedExecutors_; std::atomic<ssize_t> keepAliveCount_{1};
}; };
// Vector-like structure to play with window, // Vector-like structure to play with window,
...@@ -790,14 +790,16 @@ typename SemiFuture<T>::DeferredExecutor* SemiFuture<T>::getDeferredExecutor() ...@@ -790,14 +790,16 @@ typename SemiFuture<T>::DeferredExecutor* SemiFuture<T>::getDeferredExecutor()
} }
template <class T> template <class T>
typename SemiFuture<T>::DeferredExecutor* SemiFuture<T>::stealDeferredExecutor() folly::Executor::KeepAlive<typename SemiFuture<T>::DeferredExecutor>
const { SemiFuture<T>::stealDeferredExecutor() const {
if (auto executor = this->getExecutor()) { if (auto executor = this->getExecutor()) {
assert(dynamic_cast<DeferredExecutor*>(executor) != nullptr); assert(dynamic_cast<DeferredExecutor*>(executor) != nullptr);
auto executorKeepAlive =
folly::getKeepAliveToken(static_cast<DeferredExecutor*>(executor));
this->core_->setExecutor(nullptr); this->core_->setExecutor(nullptr);
return static_cast<DeferredExecutor*>(executor); return executorKeepAlive;
} }
return nullptr; return {};
} }
template <class T> template <class T>
...@@ -857,7 +859,7 @@ Future<T> SemiFuture<T>::via( ...@@ -857,7 +859,7 @@ Future<T> SemiFuture<T>::via(
} }
if (auto deferredExecutor = getDeferredExecutor()) { if (auto deferredExecutor = getDeferredExecutor()) {
deferredExecutor->setExecutor(executor.get()); deferredExecutor->setExecutor(executor.copy());
} }
auto newFuture = Future<T>(this->core_); auto newFuture = Future<T>(this->core_);
...@@ -886,8 +888,9 @@ SemiFuture<T>::defer(F&& func) && { ...@@ -886,8 +888,9 @@ SemiFuture<T>::defer(F&& func) && {
"defer does not support Future unwrapping"); "defer does not support Future unwrapping");
DeferredExecutor* deferredExecutor = getDeferredExecutor(); DeferredExecutor* deferredExecutor = getDeferredExecutor();
if (!deferredExecutor) { if (!deferredExecutor) {
deferredExecutor = new DeferredExecutor(); auto newDeferredExecutor = DeferredExecutor::create();
this->setExecutor(deferredExecutor); deferredExecutor = newDeferredExecutor.get();
this->setExecutor(std::move(newDeferredExecutor));
} }
auto sf = Future<T>(this->core_).thenTry(std::forward<F>(func)).semi(); auto sf = Future<T>(this->core_).thenTry(std::forward<F>(func)).semi();
...@@ -1415,22 +1418,23 @@ DeferredExecutor* getDeferredExecutor(SemiFuture<T>& future) { ...@@ -1415,22 +1418,23 @@ DeferredExecutor* getDeferredExecutor(SemiFuture<T>& future) {
} }
template <typename T> template <typename T>
DeferredExecutor* stealDeferredExecutor(SemiFuture<T>& future) { folly::Executor::KeepAlive<DeferredExecutor> stealDeferredExecutor(
SemiFuture<T>& future) {
return future.stealDeferredExecutor(); return future.stealDeferredExecutor();
} }
template <typename T> template <typename T>
DeferredExecutor* stealDeferredExecutor(Future<T>&) { folly::Executor::KeepAlive<DeferredExecutor> stealDeferredExecutor(Future<T>&) {
return nullptr; return {};
} }
template <typename... Ts> template <typename... Ts>
void stealDeferredExecutorsVariadic( void stealDeferredExecutorsVariadic(
std::vector<DeferredExecutor*>& executors, std::vector<folly::Executor::KeepAlive<DeferredExecutor>>& executors,
Ts&... ts) { Ts&... ts) {
auto foreach = [&](auto& future) { auto foreach = [&](auto& future) {
if (auto executor = stealDeferredExecutor(future)) { if (auto executor = stealDeferredExecutor(future)) {
executors.push_back(executor); executors.push_back(std::move(executor));
} }
return folly::unit; return folly::unit;
}; };
...@@ -1439,12 +1443,12 @@ void stealDeferredExecutorsVariadic( ...@@ -1439,12 +1443,12 @@ void stealDeferredExecutorsVariadic(
template <class InputIterator> template <class InputIterator>
void stealDeferredExecutors( void stealDeferredExecutors(
std::vector<DeferredExecutor*>& executors, std::vector<folly::Executor::KeepAlive<DeferredExecutor>>& executors,
InputIterator first, InputIterator first,
InputIterator last) { InputIterator last) {
for (auto it = first; it != last; ++it) { for (auto it = first; it != last; ++it) {
if (auto executor = stealDeferredExecutor(*it)) { if (auto executor = stealDeferredExecutor(*it)) {
executors.push_back(executor); executors.push_back(std::move(executor));
} }
} }
} }
...@@ -1465,7 +1469,8 @@ collectAllSemiFuture(Fs&&... fs) { ...@@ -1465,7 +1469,8 @@ collectAllSemiFuture(Fs&&... fs) {
Result results; Result results;
}; };
std::vector<futures::detail::DeferredExecutor*> executors; std::vector<folly::Executor::KeepAlive<futures::detail::DeferredExecutor>>
executors;
futures::detail::stealDeferredExecutorsVariadic(executors, fs...); futures::detail::stealDeferredExecutorsVariadic(executors, fs...);
auto ctx = std::make_shared<Context>(); auto ctx = std::make_shared<Context>();
...@@ -1513,7 +1518,8 @@ collectAllSemiFuture(InputIterator first, InputIterator last) { ...@@ -1513,7 +1518,8 @@ collectAllSemiFuture(InputIterator first, InputIterator last) {
std::vector<Try<T>> results; std::vector<Try<T>> results;
}; };
std::vector<futures::detail::DeferredExecutor*> executors; std::vector<folly::Executor::KeepAlive<futures::detail::DeferredExecutor>>
executors;
futures::detail::stealDeferredExecutors(executors, first, last); futures::detail::stealDeferredExecutors(executors, first, last);
auto ctx = std::make_shared<Context>(size_t(std::distance(first, last))); auto ctx = std::make_shared<Context>(size_t(std::distance(first, last)));
...@@ -2098,9 +2104,13 @@ void waitViaImpl( ...@@ -2098,9 +2104,13 @@ void waitViaImpl(
template <class T> template <class T>
SemiFuture<T>& SemiFuture<T>::wait() & { SemiFuture<T>& SemiFuture<T>::wait() & {
if (auto deferredExecutor = getDeferredExecutor()) { if (auto deferredExecutor = getDeferredExecutor()) {
deferredExecutor->wait(); auto waitExecutor = futures::detail::WaitExecutor::create();
deferredExecutor->runAndDestroy(); deferredExecutor->setExecutor(waitExecutor.copy());
this->core_->setExecutor(nullptr); this->core_->setExecutor(nullptr);
while (!isReady()) {
waitExecutor->drive();
}
waitExecutor->detach();
} else { } else {
futures::detail::waitImpl(*this); futures::detail::waitImpl(*this);
} }
...@@ -2115,10 +2125,16 @@ SemiFuture<T>&& SemiFuture<T>::wait() && { ...@@ -2115,10 +2125,16 @@ SemiFuture<T>&& SemiFuture<T>::wait() && {
template <class T> template <class T>
SemiFuture<T>& SemiFuture<T>::wait(Duration dur) & { SemiFuture<T>& SemiFuture<T>::wait(Duration dur) & {
if (auto deferredExecutor = getDeferredExecutor()) { if (auto deferredExecutor = getDeferredExecutor()) {
if (deferredExecutor->wait(dur)) { auto waitExecutor = futures::detail::WaitExecutor::create();
deferredExecutor->runAndDestroy(); auto deadline = futures::detail::WaitExecutor::Clock::now() + dur;
deferredExecutor->setExecutor(waitExecutor.copy());
this->core_->setExecutor(nullptr); this->core_->setExecutor(nullptr);
while (!isReady()) {
if (!waitExecutor->driveUntil(deadline)) {
break;
}
} }
waitExecutor->detach();
} else { } else {
futures::detail::waitImpl(*this, dur); futures::detail::waitImpl(*this, dur);
} }
......
...@@ -447,7 +447,8 @@ template <typename T> ...@@ -447,7 +447,8 @@ template <typename T>
DeferredExecutor* getDeferredExecutor(SemiFuture<T>& future); DeferredExecutor* getDeferredExecutor(SemiFuture<T>& future);
template <typename T> template <typename T>
DeferredExecutor* stealDeferredExecutor(SemiFuture<T>& future); folly::Executor::KeepAlive<DeferredExecutor> stealDeferredExecutor(
SemiFuture<T>& future);
} // namespace detail } // namespace detail
} // namespace futures } // namespace futures
...@@ -876,8 +877,8 @@ class SemiFuture : private futures::detail::FutureBase<T> { ...@@ -876,8 +877,8 @@ class SemiFuture : private futures::detail::FutureBase<T> {
friend class SemiFuture; friend class SemiFuture;
template <class> template <class>
friend class Future; friend class Future;
friend DeferredExecutor* futures::detail::stealDeferredExecutor<T>( friend folly::Executor::KeepAlive<DeferredExecutor>
SemiFuture&); futures::detail::stealDeferredExecutor<T>(SemiFuture&);
friend DeferredExecutor* futures::detail::getDeferredExecutor<T>(SemiFuture&); friend DeferredExecutor* futures::detail::getDeferredExecutor<T>(SemiFuture&);
using Base::setExecutor; using Base::setExecutor;
...@@ -896,7 +897,7 @@ class SemiFuture : private futures::detail::FutureBase<T> { ...@@ -896,7 +897,7 @@ class SemiFuture : private futures::detail::FutureBase<T> {
DeferredExecutor* getDeferredExecutor() const; DeferredExecutor* getDeferredExecutor() const;
// Throws FutureInvalid if !this->core_ // Throws FutureInvalid if !this->core_
DeferredExecutor* stealDeferredExecutor() const; folly::Executor::KeepAlive<DeferredExecutor> stealDeferredExecutor() const;
/// Blocks until the future is fulfilled, or `dur` elapses. /// Blocks until the future is fulfilled, or `dur` elapses.
/// ///
......
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