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

Replace type punning with boost variant in Future core.

Summary: More broadly compatible implementation of variant in future core using a boost variant.

Reviewed By: andriigrynenko

Differential Revision: D16676402

fbshipit-source-id: 19784ab1815e81b9afc49e46b5425e55111cd530
parent 85465c8e
...@@ -825,11 +825,10 @@ SemiFuture<T>::defer(F&& func) && { ...@@ -825,11 +825,10 @@ SemiFuture<T>::defer(F&& func) && {
auto deferredExecutorPtr = this->getDeferredExecutor(); auto deferredExecutorPtr = this->getDeferredExecutor();
futures::detail::KeepAliveOrDeferred deferredExecutor = [&]() { futures::detail::KeepAliveOrDeferred deferredExecutor = [&]() {
if (deferredExecutorPtr) { if (deferredExecutorPtr) {
return futures::detail::KeepAliveOrDeferred{ return futures::detail::KeepAliveOrDeferred{deferredExecutorPtr->copy()};
futures::detail::DeferredWrapper{deferredExecutorPtr}};
} else { } else {
auto newDeferredExecutor = futures::detail::KeepAliveOrDeferred( auto newDeferredExecutor = futures::detail::KeepAliveOrDeferred(
futures::detail::DeferredWrapper::create()); futures::detail::DeferredExecutor::create());
this->setExecutor(newDeferredExecutor.copy()); this->setExecutor(newDeferredExecutor.copy());
return newDeferredExecutor; return newDeferredExecutor;
} }
...@@ -851,11 +850,11 @@ SemiFuture<T>::deferExTry(F&& func) && { ...@@ -851,11 +850,11 @@ SemiFuture<T>::deferExTry(F&& func) && {
auto deferredExecutorPtr = this->getDeferredExecutor(); auto deferredExecutorPtr = this->getDeferredExecutor();
futures::detail::DeferredWrapper deferredExecutor = [&]() mutable { futures::detail::DeferredWrapper deferredExecutor = [&]() mutable {
if (deferredExecutorPtr) { if (deferredExecutorPtr) {
return futures::detail::DeferredWrapper(deferredExecutorPtr); return deferredExecutorPtr->copy();
} else { } else {
auto newDeferredExecutor = futures::detail::DeferredWrapper::create(); auto newDeferredExecutor = futures::detail::DeferredExecutor::create();
this->setExecutor( this->setExecutor(
futures::detail::KeepAliveOrDeferred{newDeferredExecutor}); futures::detail::KeepAliveOrDeferred{newDeferredExecutor->copy()});
return newDeferredExecutor; return newDeferredExecutor;
} }
}(); }();
......
...@@ -22,6 +22,8 @@ ...@@ -22,6 +22,8 @@
#include <utility> #include <utility>
#include <vector> #include <vector>
#include <boost/variant.hpp>
#include <folly/Executor.h> #include <folly/Executor.h>
#include <folly/Function.h> #include <folly/Function.h>
#include <folly/Optional.h> #include <folly/Optional.h>
...@@ -88,86 +90,13 @@ bool compare_exchange_strong_release_acquire( ...@@ -88,86 +90,13 @@ bool compare_exchange_strong_release_acquire(
class DeferredExecutor; class DeferredExecutor;
/** class UniqueDeleter {
* KeepAlive equivalent for DeferredExecutor, that is not a true executor.
* KeepAliveOrDeferred acts as a union of DeferredWrapper and KeepAlive.
*/
class DeferredWrapper {
public: public:
DeferredWrapper() = default; void operator()(DeferredExecutor* ptr);
/**
* 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);
}; };
using DeferredWrapper = std::unique_ptr<DeferredExecutor, UniqueDeleter>;
/** /**
* Wrapper type that represents either a KeepAlive or a DeferredExecutor. * 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 * Acts as if a type-safe tagged union of the two using knowledge that the two
...@@ -175,38 +104,22 @@ class DeferredWrapper { ...@@ -175,38 +104,22 @@ class DeferredWrapper {
*/ */
class KeepAliveOrDeferred { class KeepAliveOrDeferred {
public: public:
KeepAliveOrDeferred(Executor::KeepAlive<> ka) { KeepAliveOrDeferred(Executor::KeepAlive<> ka) : storage_{std::move(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()); DCHECK(!isDeferred());
} }
KeepAliveOrDeferred(DeferredWrapper deferred) { KeepAliveOrDeferred(DeferredWrapper deferred)
new (&storage_) DeferredWrapper(std::move(deferred)); : storage_{std::move(deferred)} {}
}
KeepAliveOrDeferred() {} KeepAliveOrDeferred() {}
~KeepAliveOrDeferred() { ~KeepAliveOrDeferred() {}
reset();
}
KeepAliveOrDeferred(KeepAliveOrDeferred&& other) { KeepAliveOrDeferred(KeepAliveOrDeferred&& other)
if (other.isDeferred()) { : storage_{std::move(other.storage_)} {}
new (&storage_) DeferredWrapper(std::move(other).stealDeferred());
} else {
new (&storage_) Executor::KeepAlive<>(std::move(other).stealKeepAlive());
}
}
KeepAliveOrDeferred& operator=(KeepAliveOrDeferred&& other) { KeepAliveOrDeferred& operator=(KeepAliveOrDeferred&& other) {
reset(); storage_ = std::move(other.storage_);
if (other.isDeferred()) {
new (&storage_) DeferredWrapper(std::move(other).stealDeferred());
} else {
new (&storage_) Executor::KeepAlive<>(std::move(other).stealKeepAlive());
}
return *this; return *this;
} }
...@@ -231,60 +144,46 @@ class KeepAliveOrDeferred { ...@@ -231,60 +144,46 @@ class KeepAliveOrDeferred {
return std::move(asKeepAlive()); return std::move(asKeepAlive());
} }
DeferredWrapper stealDeferred() && { std::unique_ptr<DeferredExecutor, UniqueDeleter> stealDeferred() && {
if (!isDeferred()) { if (!isDeferred()) {
return DeferredWrapper{}; return std::unique_ptr<DeferredExecutor, UniqueDeleter>{};
} }
return std::move(asDeferred()); return std::move(asDeferred());
} }
bool isDeferred() const { bool isDeferred() const {
return DeferredWrapper::representsDeferred(storage_); return boost::get<DeferredWrapper>(&storage_) != nullptr;
} }
bool isKeepAlive() const { bool isKeepAlive() const {
return !isDeferred(); return !isDeferred();
} }
KeepAliveOrDeferred copy() const { KeepAliveOrDeferred copy() const;
if (isDeferred()) {
return KeepAliveOrDeferred{std::move(asDeferred())};
} else {
return KeepAliveOrDeferred{std::move(asKeepAlive())};
}
}
explicit operator bool() const { explicit operator bool() const {
return storage_; return getDeferredExecutor() || getKeepAliveExecutor();
} }
private: private:
uint64_t storage_ = reinterpret_cast<uintptr_t>(nullptr); boost::variant<DeferredWrapper, Executor::KeepAlive<>> storage_;
friend class DeferredExecutor; friend class DeferredExecutor;
void reset() {
if (isDeferred()) {
DeferredWrapper dw = std::move(asDeferred());
} else {
Executor::KeepAlive<> ka = std::move(asKeepAlive());
}
}
Executor::KeepAlive<>& asKeepAlive() { Executor::KeepAlive<>& asKeepAlive() {
return *reinterpret_cast<Executor::KeepAlive<>*>(&storage_); return boost::get<Executor::KeepAlive<>>(storage_);
} }
const Executor::KeepAlive<>& asKeepAlive() const { const Executor::KeepAlive<>& asKeepAlive() const {
return *reinterpret_cast<const Executor::KeepAlive<>*>(&storage_); return boost::get<Executor::KeepAlive<>>(storage_);
} }
DeferredWrapper& asDeferred() { DeferredWrapper& asDeferred() {
return *reinterpret_cast<DeferredWrapper*>(&storage_); return boost::get<DeferredWrapper>(storage_);
} }
const DeferredWrapper& asDeferred() const { const DeferredWrapper& asDeferred() const {
return *reinterpret_cast<const DeferredWrapper*>(&storage_); return boost::get<DeferredWrapper>(storage_);
} }
}; };
...@@ -396,8 +295,18 @@ class DeferredExecutor final { ...@@ -396,8 +295,18 @@ class DeferredExecutor final {
std::exchange(func_, nullptr); std::exchange(func_, nullptr);
} }
DeferredWrapper copy() {
acquire();
return DeferredWrapper(this);
}
static DeferredWrapper create() {
return DeferredWrapper(new DeferredExecutor{});
}
private: private:
DeferredExecutor() {} DeferredExecutor() {}
friend class UniqueDeleter;
bool acquire() { bool acquire() {
auto keepAliveCount = auto keepAliveCount =
...@@ -421,44 +330,24 @@ class DeferredExecutor final { ...@@ -421,44 +330,24 @@ class DeferredExecutor final {
folly::Executor::KeepAlive<> executor_; folly::Executor::KeepAlive<> executor_;
std::unique_ptr<std::vector<DeferredWrapper>> nestedExecutors_; std::unique_ptr<std::vector<DeferredWrapper>> nestedExecutors_;
std::atomic<ssize_t> keepAliveCount_{1}; std::atomic<ssize_t> keepAliveCount_{1};
friend class KeepAliveOrDeferred;
friend class DeferredWrapper;
}; };
inline DeferredWrapper::DeferredWrapper(DeferredExecutor* de) inline void UniqueDeleter::operator()(DeferredExecutor* ptr) {
: storage_{reinterpret_cast<uint64_t>(de) | kDeferredFlag} { if (ptr) {
de->acquire(); ptr->release();
}
inline DeferredWrapper::~DeferredWrapper() {
if (storage_) {
steal()->release();
} }
} }
inline DeferredWrapper::DeferredWrapper(const DeferredWrapper& other) inline KeepAliveOrDeferred KeepAliveOrDeferred::copy() const {
: storage_{other.storage_} { if (isDeferred()) {
if (storage_) { if (auto def = getDeferredExecutor()) {
get()->acquire(); return KeepAliveOrDeferred{def->copy()};
} } else {
} return KeepAliveOrDeferred{};
}
inline DeferredWrapper& DeferredWrapper::operator=( } else {
const DeferredWrapper& other) { return KeepAliveOrDeferred{asKeepAlive()};
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. /// The shared state object for Future and Promise.
......
...@@ -1247,7 +1247,7 @@ TEST(SemiFuture, deferredExecutorInlineTest) { ...@@ -1247,7 +1247,7 @@ TEST(SemiFuture, deferredExecutorInlineTest) {
auto manualExec1KA = getKeepAliveToken(manualExec1); auto manualExec1KA = getKeepAliveToken(manualExec1);
auto manualExec2 = ManualExecutor{}; auto manualExec2 = ManualExecutor{};
auto manualExec2KA = getKeepAliveToken(manualExec2); auto manualExec2KA = getKeepAliveToken(manualExec2);
auto dw = futures::detail::DeferredWrapper::create(); auto dw = futures::detail::DeferredExecutor::create();
auto* de = dw.get(); auto* de = dw.get();
de->setExecutor(manualExec1KA); de->setExecutor(manualExec1KA);
de->addFrom(Executor::KeepAlive<>{}, [&](auto&&) { a = true; }); de->addFrom(Executor::KeepAlive<>{}, [&](auto&&) { a = true; });
......
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