Commit ea6ee538 authored by Lee Howes's avatar Lee Howes Committed by Facebook Github Bot

Support inline defer 4/n - Use variant of KeepAlive<> and DeferredExecutor in Core

Summary:
After this diff, DeferredExecutor participates consistently in executor inline behaviour by being special cased in the core.

DeferredExecutor is no longer an executor, and is hence no longer special cased in the Future code. This is replaced with a variant of DeferredExecutor and Executor in Core.

Reviewed By: yfeldblum, andriigrynenko

Differential Revision: D15836529

fbshipit-source-id: 8324ba1de57e85fc757ecc3b431bf71858868a0d
parent 0c0aeccc
......@@ -379,7 +379,7 @@ FutureBase<T>::thenImplementation(
// grab the Future now before we lose our handle on the Promise
auto sf = p.getSemiFuture();
sf.setExecutor(this->getExecutor());
sf.setExecutor(folly::Executor::KeepAlive<>{this->getExecutor()});
auto f = Future<B>(sf.core_);
sf.core_ = nullptr;
......@@ -726,26 +726,8 @@ SemiFuture<T> SemiFuture<T>::makeEmpty() {
}
template <class T>
typename SemiFuture<T>::DeferredExecutor* SemiFuture<T>::getDeferredExecutor()
const {
if (auto executor = this->getExecutor()) {
assert(dynamic_cast<DeferredExecutor*>(executor) != nullptr);
return static_cast<DeferredExecutor*>(executor);
}
return nullptr;
}
template <class T>
folly::Executor::KeepAlive<typename SemiFuture<T>::DeferredExecutor>
SemiFuture<T>::stealDeferredExecutor() const {
if (auto executor = this->getExecutor()) {
assert(dynamic_cast<DeferredExecutor*>(executor) != nullptr);
auto executorKeepAlive =
folly::getKeepAliveToken(static_cast<DeferredExecutor*>(executor));
this->core_->setExecutor(nullptr);
return executorKeepAlive;
}
return {};
futures::detail::DeferredWrapper SemiFuture<T>::stealDeferredExecutor() {
return this->getCore().stealDeferredExecutor();
}
template <class T>
......@@ -753,10 +735,8 @@ void SemiFuture<T>::releaseDeferredExecutor(Core* core) {
if (!core || core->hasCallback()) {
return;
}
if (auto executor = core->getExecutor()) {
assert(dynamic_cast<DeferredExecutor*>(executor) != nullptr);
static_cast<DeferredExecutor*>(executor)->detach();
core->setExecutor(nullptr);
if (auto executor = core->stealDeferredExecutor()) {
executor.get()->detach();
}
}
......@@ -774,7 +754,7 @@ SemiFuture<T>::SemiFuture(Future<T>&& other) noexcept
: futures::detail::FutureBase<T>(std::move(other)) {
// SemiFuture should not have an executor on construction
if (this->core_) {
this->setExecutor(nullptr);
this->setExecutor(futures::detail::KeepAliveOrDeferred{});
}
}
......@@ -791,7 +771,7 @@ SemiFuture<T>& SemiFuture<T>::operator=(Future<T>&& other) noexcept {
this->assign(std::move(other));
// SemiFuture should not have an executor on construction
if (this->core_) {
this->setExecutor(nullptr);
this->setExecutor(Executor::KeepAlive<>{});
}
return *this;
}
......@@ -802,7 +782,7 @@ Future<T> SemiFuture<T>::via(Executor::KeepAlive<> executor) && {
throw_exception<FutureNoExecutor>();
}
if (auto deferredExecutor = getDeferredExecutor()) {
if (auto deferredExecutor = this->getDeferredExecutor()) {
deferredExecutor->setExecutor(executor.copy());
}
......@@ -830,18 +810,24 @@ template <class T>
template <typename F>
SemiFuture<typename futures::detail::tryCallableResult<T, F>::value_type>
SemiFuture<T>::defer(F&& func) && {
DeferredExecutor* deferredExecutor = getDeferredExecutor();
if (!deferredExecutor) {
auto newDeferredExecutor = DeferredExecutor::create();
deferredExecutor = newDeferredExecutor.get();
this->setExecutor(std::move(newDeferredExecutor));
auto deferredExecutorPtr = this->getDeferredExecutor();
futures::detail::KeepAliveOrDeferred deferredExecutor = [&]() {
if (deferredExecutorPtr) {
return futures::detail::KeepAliveOrDeferred{
futures::detail::DeferredWrapper{deferredExecutorPtr}};
} else {
auto newDeferredExecutor = futures::detail::KeepAliveOrDeferred(
futures::detail::DeferredWrapper::create());
this->setExecutor(newDeferredExecutor.copy());
return newDeferredExecutor;
}
}();
auto sf = Future<T>(this->core_).thenTry(std::forward<F>(func)).semi();
this->core_ = nullptr;
// Carry deferred executor through chain as constructor from Future will
// nullify it
sf.setExecutor(deferredExecutor);
sf.setExecutor(std::move(deferredExecutor));
return sf;
}
......@@ -850,35 +836,31 @@ template <typename F>
SemiFuture<
typename futures::detail::tryExecutorCallableResult<T, F>::value_type>
SemiFuture<T>::deferExTry(F&& func) && {
DeferredExecutor* deferredExecutor = getDeferredExecutor();
if (!deferredExecutor) {
auto newDeferredExecutor = DeferredExecutor::create();
deferredExecutor = newDeferredExecutor.get();
this->setExecutor(std::move(newDeferredExecutor));
auto deferredExecutorPtr = this->getDeferredExecutor();
futures::detail::DeferredWrapper deferredExecutor = [&]() mutable {
if (deferredExecutorPtr) {
return futures::detail::DeferredWrapper(deferredExecutorPtr);
} else {
auto newDeferredExecutor = futures::detail::DeferredWrapper::create();
this->setExecutor(
futures::detail::KeepAliveOrDeferred{newDeferredExecutor});
return newDeferredExecutor;
}
}();
auto sf =
Future<T>(this->core_)
auto sf = Future<T>(this->core_)
.thenExTry([func = std::forward<F>(func)](
folly::Executor::KeepAlive<>&& keepAlive,
folly::Try<T>&& val) mutable {
// Extract the raw executor from the deferred and pass to the
// continuation
Executor* thenExDeferredExecutor = keepAlive.get();
assert(
dynamic_cast<DeferredExecutor*>(thenExDeferredExecutor) !=
nullptr);
auto innerExecutorKA = getKeepAliveToken(
static_cast<DeferredExecutor*>(thenExDeferredExecutor)
->getExecutor());
return std::forward<F>(func)(
std::move(innerExecutorKA), std::forward<decltype(val)>(val));
std::move(keepAlive), std::forward<decltype(val)>(val));
})
.semi();
this->core_ = nullptr;
// Carry deferred executor through chain as constructor from Future will
// nullify it
sf.setExecutor(deferredExecutor);
sf.setExecutor(
futures::detail::KeepAliveOrDeferred{std::move(deferredExecutor)});
return sf;
}
......@@ -1432,24 +1414,23 @@ FOLLY_ALWAYS_INLINE FOLLY_ATTR_VISIBILITY_HIDDEN void foreach(
}
template <typename T>
DeferredExecutor* getDeferredExecutor(SemiFuture<T>& future) {
futures::detail::DeferredExecutor* getDeferredExecutor(SemiFuture<T>& future) {
return future.getDeferredExecutor();
}
template <typename T>
folly::Executor::KeepAlive<DeferredExecutor> stealDeferredExecutor(
SemiFuture<T>& future) {
futures::detail::DeferredWrapper stealDeferredExecutor(SemiFuture<T>& future) {
return future.stealDeferredExecutor();
}
template <typename T>
folly::Executor::KeepAlive<DeferredExecutor> stealDeferredExecutor(Future<T>&) {
futures::detail::DeferredWrapper stealDeferredExecutor(Future<T>&) {
return {};
}
template <typename... Ts>
void stealDeferredExecutorsVariadic(
std::vector<folly::Executor::KeepAlive<DeferredExecutor>>& executors,
std::vector<futures::detail::DeferredWrapper>& executors,
Ts&... ts) {
auto foreach = [&](auto& future) {
if (auto executor = stealDeferredExecutor(future)) {
......@@ -1462,7 +1443,7 @@ void stealDeferredExecutorsVariadic(
template <class InputIterator>
void stealDeferredExecutors(
std::vector<folly::Executor::KeepAlive<DeferredExecutor>>& executors,
std::vector<futures::detail::DeferredWrapper>& executors,
InputIterator first,
InputIterator last) {
for (auto it = first; it != last; ++it) {
......@@ -1488,8 +1469,7 @@ collectAllSemiFuture(Fs&&... fs) {
Result results;
};
std::vector<folly::Executor::KeepAlive<futures::detail::DeferredExecutor>>
executors;
std::vector<futures::detail::DeferredWrapper> executors;
futures::detail::stealDeferredExecutorsVariadic(executors, fs...);
auto ctx = std::make_shared<Context>();
......@@ -1537,8 +1517,7 @@ collectAllSemiFuture(InputIterator first, InputIterator last) {
std::vector<Try<T>> results;
};
std::vector<folly::Executor::KeepAlive<futures::detail::DeferredExecutor>>
executors;
std::vector<futures::detail::DeferredWrapper> executors;
futures::detail::stealDeferredExecutors(executors, first, last);
auto ctx = std::make_shared<Context>(size_t(std::distance(first, last)));
......@@ -1599,8 +1578,7 @@ collectSemiFuture(InputIterator first, InputIterator last) {
std::atomic<bool> threw{false};
};
std::vector<folly::Executor::KeepAlive<futures::detail::DeferredExecutor>>
executors;
std::vector<futures::detail::DeferredWrapper> executors;
futures::detail::stealDeferredExecutors(executors, first, last);
auto ctx = std::make_shared<Context>(std::distance(first, last));
......@@ -1622,7 +1600,7 @@ collectSemiFuture(InputIterator first, InputIterator last) {
return std::move(t).value();
};
future = std::move(future).defer(work);
auto deferredExecutor = futures::detail::getDeferredExecutor(future);
const auto& deferredExecutor = futures::detail::getDeferredExecutor(future);
deferredExecutor->setNestedExecutors(std::move(executors));
}
return future;
......@@ -1653,8 +1631,7 @@ collectSemiFuture(Fs&&... fs) {
std::atomic<bool> threw{false};
};
std::vector<folly::Executor::KeepAlive<futures::detail::DeferredExecutor>>
executors;
std::vector<futures::detail::DeferredWrapper> executors;
futures::detail::stealDeferredExecutorsVariadic(executors, fs...);
auto ctx = std::make_shared<Context>();
......@@ -1678,7 +1655,7 @@ collectSemiFuture(Fs&&... fs) {
return std::move(t).value();
};
future = std::move(future).defer(work);
auto deferredExecutor = futures::detail::getDeferredExecutor(future);
const auto& deferredExecutor = futures::detail::getDeferredExecutor(future);
deferredExecutor->setNestedExecutors(std::move(executors));
}
return future;
......@@ -1720,8 +1697,7 @@ collectAnySemiFuture(InputIterator first, InputIterator last) {
std::atomic<bool> done{false};
};
std::vector<folly::Executor::KeepAlive<futures::detail::DeferredExecutor>>
executors;
std::vector<futures::detail::DeferredWrapper> executors;
futures::detail::stealDeferredExecutors(executors, first, last);
auto ctx = std::make_shared<Context>();
......@@ -1738,7 +1714,7 @@ collectAnySemiFuture(InputIterator first, InputIterator last) {
[](Try<typename decltype(future)::value_type>&& t) {
return std::move(t).value();
});
auto deferredExecutor = futures::detail::getDeferredExecutor(future);
const auto& deferredExecutor = futures::detail::getDeferredExecutor(future);
deferredExecutor->setNestedExecutors(std::move(executors));
}
return future;
......@@ -1762,8 +1738,7 @@ collectAnyWithoutException(InputIterator first, InputIterator last) {
size_t nTotal;
};
std::vector<folly::Executor::KeepAlive<futures::detail::DeferredExecutor>>
executors;
std::vector<futures::detail::DeferredWrapper> executors;
futures::detail::stealDeferredExecutors(executors, first, last);
auto ctx = std::make_shared<Context>(size_t(std::distance(first, last)));
......@@ -1786,7 +1761,7 @@ collectAnyWithoutException(InputIterator first, InputIterator last) {
[](Try<typename decltype(future)::value_type>&& t) {
return std::move(t).value();
});
auto deferredExecutor = futures::detail::getDeferredExecutor(future);
const auto& deferredExecutor = futures::detail::getDeferredExecutor(future);
deferredExecutor->setNestedExecutors(std::move(executors));
}
return future;
......@@ -1822,8 +1797,7 @@ collectN(InputIterator first, InputIterator last, size_t n) {
exception_wrapper(std::runtime_error("Not enough futures")));
}
std::vector<folly::Executor::KeepAlive<futures::detail::DeferredExecutor>>
executors;
std::vector<futures::detail::DeferredWrapper> executors;
futures::detail::stealDeferredExecutors(executors, first, last);
// for each completed Future, increase count and add to vector, until we
......@@ -1863,7 +1837,7 @@ collectN(InputIterator first, InputIterator last, size_t n) {
[](Try<typename decltype(future)::value_type>&& t) {
return std::move(t).value();
});
auto deferredExecutor = futures::detail::getDeferredExecutor(future);
const auto& deferredExecutor = futures::detail::getDeferredExecutor(future);
deferredExecutor->setNestedExecutors(std::move(executors));
}
return future;
......@@ -2221,7 +2195,7 @@ void waitViaImpl(
template <class T>
SemiFuture<T>& SemiFuture<T>::wait() & {
if (auto deferredExecutor = getDeferredExecutor()) {
if (auto deferredExecutor = this->getDeferredExecutor()) {
// Make sure that the last callback in the future chain will be run on the
// WaitExecutor.
Promise<T> promise;
......@@ -2251,7 +2225,7 @@ SemiFuture<T>&& SemiFuture<T>::wait() && {
template <class T>
SemiFuture<T>& SemiFuture<T>::wait(Duration dur) & {
if (auto deferredExecutor = getDeferredExecutor()) {
if (auto deferredExecutor = this->getDeferredExecutor()) {
// Make sure that the last callback in the future chain will be run on the
// WaitExecutor.
Promise<T> promise;
......
......@@ -407,11 +407,15 @@ class FutureBase {
return getCore().getExecutor();
}
DeferredExecutor* getDeferredExecutor() const {
return getCore().getDeferredExecutor();
}
// Sets the Executor within the Core state object of `this`.
// Must be called either before attaching a callback or after the callback
// has already been invoked, but not concurrently with anything which might
// trigger invocation of the callback.
void setExecutor(Executor::KeepAlive<> x) {
void setExecutor(futures::detail::KeepAliveOrDeferred x) {
getCore().setExecutor(std::move(x));
}
......@@ -439,8 +443,7 @@ template <typename T>
DeferredExecutor* getDeferredExecutor(SemiFuture<T>& future);
template <typename T>
folly::Executor::KeepAlive<DeferredExecutor> stealDeferredExecutor(
SemiFuture<T>& future);
futures::detail::DeferredWrapper stealDeferredExecutor(SemiFuture<T>& future);
} // namespace detail
} // namespace futures
......@@ -795,8 +798,9 @@ class SemiFuture : private futures::detail::FutureBase<T> {
if (deferredExecutor) {
ret =
std::move(ret).defer([](Try<T>&& t) { return std::move(t).value(); });
ret.getDeferredExecutor()->setNestedExecutors(
{std::move(deferredExecutor)});
std::vector<futures::detail::DeferredWrapper> des;
des.push_back(std::move(deferredExecutor));
ret.getDeferredExecutor()->setNestedExecutors(std::move(des));
}
return ret;
}
......@@ -852,9 +856,10 @@ class SemiFuture : private futures::detail::FutureBase<T> {
friend class SemiFuture;
template <class>
friend class Future;
friend folly::Executor::KeepAlive<DeferredExecutor>
futures::detail::stealDeferredExecutor<T>(SemiFuture&);
friend DeferredExecutor* futures::detail::getDeferredExecutor<T>(SemiFuture&);
friend futures::detail::DeferredWrapper
futures::detail::stealDeferredExecutor<T>(SemiFuture<T>&);
friend DeferredExecutor* futures::detail::getDeferredExecutor<T>(
SemiFuture<T>&);
using Base::setExecutor;
using Base::throwIfInvalid;
......@@ -869,10 +874,7 @@ class SemiFuture : private futures::detail::FutureBase<T> {
: Base(futures::detail::EmptyConstruct{}) {}
// Throws FutureInvalid if !this->core_
DeferredExecutor* getDeferredExecutor() const;
// Throws FutureInvalid if !this->core_
folly::Executor::KeepAlive<DeferredExecutor> stealDeferredExecutor() const;
futures::detail::DeferredWrapper stealDeferredExecutor();
/// Blocks until the future is fulfilled, or `dur` elapses.
///
......
......@@ -86,24 +86,213 @@ bool compare_exchange_strong_release_acquire(
expected, desired, std::memory_order_release, std::memory_order_acquire);
}
class DeferredExecutor;
/**
* Defer work until executor is actively boosted.
*
* NOTE: that this executor is a private implementation detail belonging to the
* Folly Futures library and not intended to be used elsewhere. It is designed
* specifically for the use case of deferring work on a SemiFuture. It is NOT
* thread safe. Please do not use for any other purpose without great care.
* KeepAlive equivalent for DeferredExecutor, that is not a true executor.
* KeepAliveOrDeferred acts as a union of DeferredWrapper and KeepAlive.
*/
class DeferredExecutor final : public Executor {
class DeferredWrapper {
public:
void add(Func func) override {
addFrom(
Executor::KeepAlive<>{},
[func = std::move(func)](Executor::KeepAlive<>&& /*ka*/) mutable {
func();
});
DeferredWrapper() = default;
/**
* Constructs a DeferredWrapper by acquiring the executor.
*/
explicit DeferredWrapper(DeferredExecutor* de);
DeferredWrapper(const DeferredWrapper& other);
DeferredWrapper(DeferredWrapper&& other)
: storage_{std::exchange(
other.storage_,
reinterpret_cast<uint64_t>(nullptr))} {}
DeferredWrapper& operator=(const DeferredWrapper& other);
DeferredWrapper& operator=(DeferredWrapper&& other) {
storage_ =
std::exchange(other.storage_, reinterpret_cast<uint64_t>(nullptr));
return *this;
}
DeferredExecutor* steal() {
uint64_t oldStorage =
std::exchange(storage_, reinterpret_cast<uint64_t>(nullptr));
return reinterpret_cast<DeferredExecutor*>(oldStorage & ~kDeferredFlag);
}
DeferredExecutor* get() const {
return reinterpret_cast<DeferredExecutor*>(storage_ & ~kDeferredFlag);
}
~DeferredWrapper();
explicit operator bool() const {
return storage_;
}
/**
* Check the passed value against the deferred flag and return true if it
* represents a DeferredWrapper.
*/
static bool representsDeferred(const uint64_t& storage) {
return (storage & kDeferredFlag) != 0;
}
static DeferredWrapper create();
// Bit to represent a deferred executor rather than a KeepAlive.
// Deferred bit will be bit 3 on 64-bit platforms, where pointers align to 8
// bytes so bit 2 is free. On 32-bit platforms pointers may align to 4 bytes
// so we have to use the rest of the uint64_t. On 32-bit big endian
// platforms where the pointer will sit at the high end of the uint64_t,
// also use bit 2. On little-endian 32-bit platforms use a bit above the
// size of the 32-bit pointer.
static constexpr uint64_t kDeferredFlag = uint64_t(1)
<< (std::numeric_limits<uintptr_t>::digits >= 64
? 2
: (std::numeric_limits<uintptr_t>::digits + 2));
static_assert(
!(std::numeric_limits<uintptr_t>::digits == 32 && kIsBigEndian),
"Big-endian 32-bit systems untested for Futures code.");
static_assert(
sizeof(uint64_t) >= sizeof(uintptr_t),
"Sanity check on pointer size");
static_assert(
(static_cast<uint64_t>(folly::detail::ExecutorKeepAliveBase::kFlagMask) &
kDeferredFlag) == 0,
"Nothing in the current executor mask can use the deferred bit.");
private:
uint64_t storage_ = reinterpret_cast<uint64_t>(nullptr);
};
/**
* Wrapper type that represents either a KeepAlive or a DeferredExecutor.
* Acts as if a type-safe tagged union of the two using knowledge that the two
* can safely be distinguished.
*/
class KeepAliveOrDeferred {
public:
KeepAliveOrDeferred(Executor::KeepAlive<> ka) {
new (&storage_) Executor::KeepAlive<>(std::move(ka));
// Verify that the deferred bit is not set. If it were, KeepAlive and
// DeferredExecutor would be impossible to distinguish.
DCHECK(!isDeferred());
}
KeepAliveOrDeferred(DeferredWrapper deferred) {
new (&storage_) DeferredWrapper(std::move(deferred));
}
KeepAliveOrDeferred() {}
~KeepAliveOrDeferred() {
reset();
}
KeepAliveOrDeferred(KeepAliveOrDeferred&& other) {
if (other.isDeferred()) {
new (&storage_) DeferredWrapper(std::move(other).stealDeferred());
} else {
new (&storage_) Executor::KeepAlive<>(std::move(other).stealKeepAlive());
}
}
KeepAliveOrDeferred& operator=(KeepAliveOrDeferred&& other) {
reset();
if (other.isDeferred()) {
new (&storage_) DeferredWrapper(std::move(other).stealDeferred());
} else {
new (&storage_) Executor::KeepAlive<>(std::move(other).stealKeepAlive());
}
return *this;
}
DeferredExecutor* getDeferredExecutor() const {
if (!isDeferred()) {
return nullptr;
}
return asDeferred().get();
}
Executor* getKeepAliveExecutor() const {
if (isDeferred()) {
return nullptr;
}
return asKeepAlive().get();
}
Executor::KeepAlive<> stealKeepAlive() && {
if (isDeferred()) {
return Executor::KeepAlive<>{};
}
return std::move(asKeepAlive());
}
DeferredWrapper stealDeferred() && {
if (!isDeferred()) {
return DeferredWrapper{};
}
return std::move(asDeferred());
}
bool isDeferred() const {
return DeferredWrapper::representsDeferred(storage_);
}
bool isKeepAlive() const {
return !isDeferred();
}
KeepAliveOrDeferred copy() const {
if (isDeferred()) {
return KeepAliveOrDeferred{std::move(asDeferred())};
} else {
return KeepAliveOrDeferred{std::move(asKeepAlive())};
}
}
explicit operator bool() const {
return storage_;
}
private:
uint64_t storage_ = reinterpret_cast<uintptr_t>(nullptr);
friend class DeferredExecutor;
void reset() {
if (isDeferred()) {
DeferredWrapper dw = std::move(asDeferred());
} else {
Executor::KeepAlive<> ka = std::move(asKeepAlive());
}
}
Executor::KeepAlive<>& asKeepAlive() {
return *reinterpret_cast<Executor::KeepAlive<>*>(&storage_);
}
const Executor::KeepAlive<>& asKeepAlive() const {
return *reinterpret_cast<const Executor::KeepAlive<>*>(&storage_);
}
DeferredWrapper& asDeferred() {
return *reinterpret_cast<DeferredWrapper*>(&storage_);
}
const DeferredWrapper& asDeferred() const {
return *reinterpret_cast<const DeferredWrapper*>(&storage_);
}
};
/**
* Defer work until executor is actively boosted.
*/
class DeferredExecutor final {
public:
// addFrom will:
// * run func inline if there is a stored executor and completingKA matches
// the stored executor
......@@ -158,7 +347,8 @@ class DeferredExecutor final : public Executor {
if (nestedExecutors_) {
auto nestedExecutors = std::exchange(nestedExecutors_, nullptr);
for (auto& nestedExecutor : *nestedExecutors) {
nestedExecutor->setExecutor(executor.copy());
assert(nestedExecutor.get());
nestedExecutor.get()->setExecutor(executor.copy());
}
}
executor_ = std::move(executor);
......@@ -177,11 +367,18 @@ class DeferredExecutor final : public Executor {
executor_.copy().add(std::exchange(func_, nullptr));
}
void setNestedExecutors(std::vector<DeferredWrapper> executors) {
DCHECK(!nestedExecutors_);
nestedExecutors_ =
std::make_unique<std::vector<DeferredWrapper>>(std::move(executors));
}
void detach() {
if (nestedExecutors_) {
auto nestedExecutors = std::exchange(nestedExecutors_, nullptr);
for (auto& nestedExecutor : *nestedExecutors) {
nestedExecutor->detach();
assert(nestedExecutor.get());
nestedExecutor.get()->detach();
}
}
auto state = state_.load(std::memory_order_acquire);
......@@ -199,29 +396,17 @@ class DeferredExecutor final : public Executor {
std::exchange(func_, nullptr);
}
void setNestedExecutors(
std::vector<folly::Executor::KeepAlive<DeferredExecutor>> executors) {
DCHECK(!nestedExecutors_);
nestedExecutors_ = std::make_unique<
std::vector<folly::Executor::KeepAlive<DeferredExecutor>>>(
std::move(executors));
}
static KeepAlive<DeferredExecutor> create() {
return makeKeepAlive<DeferredExecutor>(new DeferredExecutor());
}
private:
DeferredExecutor() {}
bool keepAliveAcquire() override {
bool acquire() {
auto keepAliveCount =
keepAliveCount_.fetch_add(1, std::memory_order_relaxed);
DCHECK(keepAliveCount > 0);
return true;
}
void keepAliveRelease() override {
void release() {
auto keepAliveCount =
keepAliveCount_.fetch_sub(1, std::memory_order_acq_rel);
DCHECK(keepAliveCount > 0);
......@@ -234,11 +419,48 @@ class DeferredExecutor final : public Executor {
std::atomic<State> state_{State::EMPTY};
Executor::KeepAlive<>::KeepAliveFunc func_;
folly::Executor::KeepAlive<> executor_;
std::unique_ptr<std::vector<folly::Executor::KeepAlive<DeferredExecutor>>>
nestedExecutors_;
std::unique_ptr<std::vector<DeferredWrapper>> nestedExecutors_;
std::atomic<ssize_t> keepAliveCount_{1};
friend class KeepAliveOrDeferred;
friend class DeferredWrapper;
};
inline DeferredWrapper::DeferredWrapper(DeferredExecutor* de)
: storage_{reinterpret_cast<uint64_t>(de) | kDeferredFlag} {
de->acquire();
}
inline DeferredWrapper::~DeferredWrapper() {
if (storage_) {
steal()->release();
}
}
inline DeferredWrapper::DeferredWrapper(const DeferredWrapper& other)
: storage_{other.storage_} {
if (storage_) {
get()->acquire();
}
}
inline DeferredWrapper& DeferredWrapper::operator=(
const DeferredWrapper& other) {
storage_ = other.storage_;
if (storage_) {
get()->acquire();
}
return *this;
}
/* static */
inline DeferredWrapper DeferredWrapper::create() {
DeferredWrapper dw{};
auto* de = new DeferredExecutor{};
dw.storage_ = reinterpret_cast<uint64_t>(de) | kDeferredFlag;
return dw;
}
/// The shared state object for Future and Promise.
///
/// Nomenclature:
......@@ -619,7 +841,7 @@ class Core final {
/// Call only from consumer thread, either before attaching a callback or
/// after the callback has already been invoked, but not concurrently with
/// anything which might trigger invocation of the callback.
void setExecutor(Executor::KeepAlive<> x) {
void setExecutor(KeepAliveOrDeferred&& x) {
DCHECK(
state_ != State::OnlyCallback &&
state_ != State::OnlyCallbackAllowInline);
......@@ -627,7 +849,26 @@ class Core final {
}
Executor* getExecutor() const {
return executor_.get();
if (!executor_.isKeepAlive()) {
return nullptr;
}
return executor_.getKeepAliveExecutor();
}
DeferredExecutor* getDeferredExecutor() const {
if (!executor_.isDeferred()) {
return {};
}
return executor_.getDeferredExecutor();
}
DeferredWrapper stealDeferredExecutor() {
if (executor_.isKeepAlive()) {
return {};
}
return std::move(executor_).stealDeferred();
}
/// Call only from consumer thread
......@@ -759,13 +1000,32 @@ class Core final {
void doCallback(Executor::KeepAlive<>&& completingKA, State priorState) {
DCHECK(state_ == State::Done);
auto executor = std::exchange(executor_, Executor::KeepAlive<>());
bool allowInline =
(executor.get() == completingKA.get() &&
priorState == State::OnlyCallbackAllowInline);
// If we have no executor or if we are allowing inline executor on a
// matching executor, run inline.
if (executor && !allowInline) {
auto executor = std::exchange(executor_, KeepAliveOrDeferred{});
// Customise inline behaviour
// If addCompletingKA is non-null, then we are allowing inline execution
auto doAdd = [](Executor::KeepAlive<>&& addCompletingKA,
KeepAliveOrDeferred&& currentExecutor,
auto&& keepAliveFunc) mutable {
if (auto deferredExecutorPtr = currentExecutor.getDeferredExecutor()) {
deferredExecutorPtr->addFrom(
std::move(addCompletingKA), std::move(keepAliveFunc));
} else {
// If executors match call inline
auto currentKeepAlive = std::move(currentExecutor).stealKeepAlive();
if (addCompletingKA.get() == currentKeepAlive.get()) {
keepAliveFunc(std::move(currentKeepAlive));
} else {
std::move(currentKeepAlive).add(std::move(keepAliveFunc));
}
}
};
if (executor) {
// If we are not allowing inline, clear the completing KA to disallow
if (!(priorState == State::OnlyCallbackAllowInline)) {
completingKA = Executor::KeepAlive<>{};
}
exception_wrapper ew;
// We need to reset `callback_` after it was executed (which can happen
// through the executor or, if `Executor::add` throws, below). The
......@@ -781,8 +1041,11 @@ class Core final {
CoreAndCallbackReference guard_local_scope(this);
CoreAndCallbackReference guard_lambda(this);
try {
std::move(executor).add([core_ref = std::move(guard_lambda)](
Executor::KeepAlive<>&& ka) mutable {
doAdd(
std::move(completingKA),
std::move(executor),
[core_ref =
std::move(guard_lambda)](Executor::KeepAlive<>&& ka) mutable {
auto cr = std::move(core_ref);
Core* const core = cr.getCore();
RequestContextScopeGuard rctx(std::move(core->context_));
......@@ -806,7 +1069,7 @@ class Core final {
detachOne();
};
RequestContextScopeGuard rctx(std::move(context_));
callback_(std::move(executor), std::move(result_));
callback_(std::move(completingKA), std::move(result_));
}
}
......@@ -858,14 +1121,14 @@ class Core final {
std::atomic<unsigned char> callbackReferences_{0};
std::atomic<bool> interruptHandlerSet_{false};
SpinLock interruptLock_;
Executor::KeepAlive<> executor_;
KeepAliveOrDeferred executor_;
union {
Context context_;
};
std::unique_ptr<exception_wrapper> interrupt_{};
std::function<void(exception_wrapper const&)> interruptHandler_{nullptr};
};
} // namespace detail
} // namespace futures
} // namespace folly
......@@ -1247,9 +1247,10 @@ TEST(SemiFuture, deferredExecutorInlineTest) {
auto manualExec1KA = getKeepAliveToken(manualExec1);
auto manualExec2 = ManualExecutor{};
auto manualExec2KA = getKeepAliveToken(manualExec2);
auto de = futures::detail::DeferredExecutor::create();
auto dw = futures::detail::DeferredWrapper::create();
auto* de = dw.get();
de->setExecutor(manualExec1KA);
de->add([&]() { a = true; });
de->addFrom(Executor::KeepAlive<>{}, [&](auto&&) { a = true; });
EXPECT_FALSE(a);
manualExec1.run();
EXPECT_TRUE(a);
......
......@@ -249,7 +249,7 @@ TEST(Via, then2) {
TEST(Via, allowInline) {
ManualExecutor x1, x2;
bool a = false, b = false, c = false, d = false, e = false, f = false,
g = false, h = false, i = false, j = false;
g = false, h = false, i = false, j = false, k = false, l = false;
via(&x1)
.thenValue([&](auto&&) { a = true; })
.thenTryInline([&](auto&&) { b = true; })
......@@ -265,7 +265,11 @@ TEST(Via, allowInline) {
h = true;
return via(&x1).thenValue([&](auto&&) { i = true; });
})
.thenValueInline([&](auto&&) { j = true; });
.thenValueInline([&](auto&&) { j = true; })
.semi()
.deferValue([&](auto&&) { k = true; })
.via(&x2)
.thenValueInline([&](auto&&) { l = true; });
EXPECT_FALSE(a);
EXPECT_FALSE(b);
......@@ -307,8 +311,16 @@ TEST(Via, allowInline) {
EXPECT_TRUE(i);
EXPECT_FALSE(j);
// Deferred work is not inline so k will remain false
x2.run();
EXPECT_TRUE(j);
EXPECT_FALSE(k);
// Deferred work is not inline, but subsequent inline work should be inlined
// consistently with deferred work.
x2.run();
EXPECT_TRUE(k);
EXPECT_TRUE(l);
}
#ifndef __APPLE__ // TODO #7372389
......
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