Commit ed2cb6d2 authored by Eric Niebler's avatar Eric Niebler Committed by Facebook GitHub Bot

replace the use of boost::variant with a simple union

Summary:
`boost::variant` is an expensive template, and `boost/variant.hpp` is an expensive header. In the one place it is used in futures/detail/Core.h (a commonly-included header), it can be trivially replaced with a `union`, so do so.

As a drive-by, I mark as `noexcept` those members of `KeepAliveOrDeferred` that can be done so unconditionally.

Reviewed By: yfeldblum, kirkshoop, ot

Differential Revision: D22460453

fbshipit-source-id: 5a01f873058273d1a20265507d87796450cc008b
parent e4df98ab
...@@ -14,7 +14,10 @@ ...@@ -14,7 +14,10 @@
* limitations under the License. * limitations under the License.
*/ */
#include <new>
#include <folly/futures/detail/Core.h> #include <folly/futures/detail/Core.h>
#include <folly/lang/Assume.h>
namespace folly { namespace folly {
namespace futures { namespace futures {
...@@ -26,96 +29,110 @@ void UniqueDeleter::operator()(DeferredExecutor* ptr) { ...@@ -26,96 +29,110 @@ void UniqueDeleter::operator()(DeferredExecutor* ptr) {
} }
} }
KeepAliveOrDeferred::KeepAliveOrDeferred() = default; KeepAliveOrDeferred::KeepAliveOrDeferred() noexcept : state_(State::Deferred) {
::new (&deferred_) DW{};
}
KeepAliveOrDeferred::KeepAliveOrDeferred(Executor::KeepAlive<> ka) KeepAliveOrDeferred::KeepAliveOrDeferred(KA ka) noexcept
: storage_{std::move(ka)} { : state_(State::KeepAlive) {
DCHECK(!isDeferred()); ::new (&keepAlive_) KA{std::move(ka)};
} }
KeepAliveOrDeferred::KeepAliveOrDeferred(DeferredWrapper deferred) KeepAliveOrDeferred::KeepAliveOrDeferred(DW deferred) noexcept
: storage_{std::move(deferred)} {} : state_(State::Deferred) {
::new (&deferred_) DW{std::move(deferred)};
}
KeepAliveOrDeferred::KeepAliveOrDeferred(KeepAliveOrDeferred&& other) noexcept KeepAliveOrDeferred::KeepAliveOrDeferred(KeepAliveOrDeferred&& other) noexcept
: storage_{std::move(other.storage_)} {} : state_(other.state_) {
switch (state_) {
case State::Deferred:
::new (&deferred_) DW{std::move(other.deferred_)};
break;
case State::KeepAlive:
::new (&keepAlive_) KA{std::move(other.keepAlive_)};
break;
}
}
KeepAliveOrDeferred::~KeepAliveOrDeferred() = default; KeepAliveOrDeferred::~KeepAliveOrDeferred() {
switch (state_) {
case State::Deferred:
deferred_.~DW();
break;
case State::KeepAlive:
keepAlive_.~KA();
break;
}
}
KeepAliveOrDeferred& KeepAliveOrDeferred::operator=( KeepAliveOrDeferred& KeepAliveOrDeferred::operator=(
KeepAliveOrDeferred&& other) { KeepAliveOrDeferred&& other) noexcept {
storage_ = std::move(other.storage_); // This is safe to do because KeepAliveOrDeferred is nothrow
// move-constructible.
this->~KeepAliveOrDeferred();
::new (this) KeepAliveOrDeferred{std::move(other)};
return *this; return *this;
} }
DeferredExecutor* KeepAliveOrDeferred::getDeferredExecutor() const { DeferredExecutor* KeepAliveOrDeferred::getDeferredExecutor() const noexcept {
if (!isDeferred()) { switch (state_) {
case State::Deferred:
return deferred_.get();
case State::KeepAlive:
return nullptr; return nullptr;
} }
return asDeferred().get(); assume_unreachable();
} }
Executor* KeepAliveOrDeferred::getKeepAliveExecutor() const { Executor* KeepAliveOrDeferred::getKeepAliveExecutor() const noexcept {
if (isDeferred()) { switch (state_) {
case State::Deferred:
return nullptr; return nullptr;
case State::KeepAlive:
return keepAlive_.get();
} }
return asKeepAlive().get(); assume_unreachable();
} }
Executor::KeepAlive<> KeepAliveOrDeferred::stealKeepAlive() && { KeepAliveOrDeferred::KA KeepAliveOrDeferred::stealKeepAlive() && noexcept {
if (isDeferred()) { switch (state_) {
return Executor::KeepAlive<>{}; case State::Deferred:
return KA{};
case State::KeepAlive:
return std::move(keepAlive_);
} }
return std::move(asKeepAlive()); assume_unreachable();
} }
std::unique_ptr<DeferredExecutor, UniqueDeleter> KeepAliveOrDeferred::DW KeepAliveOrDeferred::stealDeferred() && noexcept {
KeepAliveOrDeferred::stealDeferred() && { switch (state_) {
if (!isDeferred()) { case State::Deferred:
return std::unique_ptr<DeferredExecutor, UniqueDeleter>{}; return std::move(deferred_);
case State::KeepAlive:
return DW{};
} }
return std::move(asDeferred()); assume_unreachable();
}
bool KeepAliveOrDeferred::isDeferred() const {
return boost::get<DeferredWrapper>(&storage_) != nullptr;
}
bool KeepAliveOrDeferred::isKeepAlive() const {
return !isDeferred();
} }
KeepAliveOrDeferred KeepAliveOrDeferred::copy() const { KeepAliveOrDeferred KeepAliveOrDeferred::copy() const {
if (isDeferred()) { switch (state_) {
case State::Deferred:
if (auto def = getDeferredExecutor()) { if (auto def = getDeferredExecutor()) {
return KeepAliveOrDeferred{def->copy()}; return KeepAliveOrDeferred{def->copy()};
} else { } else {
return KeepAliveOrDeferred{}; return KeepAliveOrDeferred{};
} }
} else { case State::KeepAlive:
return KeepAliveOrDeferred{asKeepAlive()}; return KeepAliveOrDeferred{keepAlive_};
} }
assume_unreachable();
} }
/* explicit */ KeepAliveOrDeferred::operator bool() const { /* explicit */ KeepAliveOrDeferred::operator bool() const noexcept {
return getDeferredExecutor() || getKeepAliveExecutor(); return getDeferredExecutor() || getKeepAliveExecutor();
} }
Executor::KeepAlive<>& KeepAliveOrDeferred::asKeepAlive() {
return boost::get<Executor::KeepAlive<>>(storage_);
}
const Executor::KeepAlive<>& KeepAliveOrDeferred::asKeepAlive() const {
return boost::get<Executor::KeepAlive<>>(storage_);
}
DeferredWrapper& KeepAliveOrDeferred::asDeferred() {
return boost::get<DeferredWrapper>(storage_);
}
const DeferredWrapper& KeepAliveOrDeferred::asDeferred() const {
return boost::get<DeferredWrapper>(storage_);
}
void DeferredExecutor::addFrom( void DeferredExecutor::addFrom(
Executor::KeepAlive<>&& completingKA, Executor::KeepAlive<>&& completingKA,
Executor::KeepAlive<>::KeepAliveFunc func) { Executor::KeepAlive<>::KeepAliveFunc func) {
......
...@@ -22,8 +22,6 @@ ...@@ -22,8 +22,6 @@
#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>
...@@ -90,43 +88,53 @@ using DeferredWrapper = std::unique_ptr<DeferredExecutor, UniqueDeleter>; ...@@ -90,43 +88,53 @@ using DeferredWrapper = std::unique_ptr<DeferredExecutor, UniqueDeleter>;
* can safely be distinguished. * can safely be distinguished.
*/ */
class KeepAliveOrDeferred { class KeepAliveOrDeferred {
private:
using KA = Executor::KeepAlive<>;
using DW = DeferredWrapper;
public: public:
KeepAliveOrDeferred(); KeepAliveOrDeferred() noexcept;
/* implicit */ KeepAliveOrDeferred(Executor::KeepAlive<> ka); /* implicit */ KeepAliveOrDeferred(KA ka) noexcept;
/* implicit */ KeepAliveOrDeferred(DeferredWrapper deferred); /* implicit */ KeepAliveOrDeferred(DW deferred) noexcept;
KeepAliveOrDeferred(KeepAliveOrDeferred&& other) noexcept; KeepAliveOrDeferred(KeepAliveOrDeferred&& other) noexcept;
~KeepAliveOrDeferred(); ~KeepAliveOrDeferred();
KeepAliveOrDeferred& operator=(KeepAliveOrDeferred&& other); KeepAliveOrDeferred& operator=(KeepAliveOrDeferred&& other) noexcept;
DeferredExecutor* getDeferredExecutor() const; DeferredExecutor* getDeferredExecutor() const noexcept;
Executor* getKeepAliveExecutor() const; Executor* getKeepAliveExecutor() const noexcept;
Executor::KeepAlive<> stealKeepAlive() &&; KA stealKeepAlive() && noexcept;
std::unique_ptr<DeferredExecutor, UniqueDeleter> stealDeferred() &&; DW stealDeferred() && noexcept;
bool isDeferred() const; bool isDeferred() const noexcept;
bool isKeepAlive() const; bool isKeepAlive() const noexcept;
KeepAliveOrDeferred copy() const; KeepAliveOrDeferred copy() const;
explicit operator bool() const; explicit operator bool() const noexcept;
private: private:
friend class DeferredExecutor; friend class DeferredExecutor;
Executor::KeepAlive<>& asKeepAlive(); enum class State { Deferred, KeepAlive } state_;
const Executor::KeepAlive<>& asKeepAlive() const; union {
DW deferred_;
KA keepAlive_;
};
};
DeferredWrapper& asDeferred(); inline bool KeepAliveOrDeferred::isDeferred() const noexcept {
const DeferredWrapper& asDeferred() const; return state_ == State::Deferred;
}
boost::variant<DeferredWrapper, Executor::KeepAlive<>> storage_; inline bool KeepAliveOrDeferred::isKeepAlive() const noexcept {
}; return state_ == State::KeepAlive;
}
/** /**
* Defer work until executor is actively boosted. * Defer work until executor is actively boosted.
......
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