namespace futures {
namespace {
- template <class Z, class F, class... Callbacks>
- Future<Z> chainHelper(F, Callbacks...);
-
template <class Z>
Future<Z> chainHelper(Future<Z> f) {
return f;
and that transition can happen immediately after transitioning from Only*
to Armed, if it is active (the usual case).
*/
-enum class State {
+enum class State : uint8_t {
Start,
OnlyResult,
OnlyCallback,
/// doesn't access a Future or Promise object from more than one thread at a
/// time there won't be any problems.
template<typename T>
-class Core : protected FSM<State> {
+class Core {
public:
/// This must be heap-constructed. There's probably a way to enforce that in
/// code but since this is just internal detail code and I don't know how
/// off-hand, I'm punting.
- Core() : FSM<State>(State::Start) {}
+ Core() {}
~Core() {
assert(detached_ == 2);
}
/// May call from any thread
bool hasResult() const {
- switch (getState()) {
+ switch (fsm_.getState()) {
case State::OnlyResult:
case State::Armed:
case State::Done:
callback_ = std::move(func);
};
- FSM_START
+ FSM_START(fsm_)
case State::Start:
- FSM_UPDATE(State::OnlyCallback, setCallback_);
+ FSM_UPDATE(fsm_, State::OnlyCallback, setCallback_);
break;
case State::OnlyResult:
- FSM_UPDATE(State::Armed, setCallback_);
+ FSM_UPDATE(fsm_, State::Armed, setCallback_);
transitionToArmed = true;
break;
void setResult(Try<T>&& t) {
bool transitionToArmed = false;
auto setResult_ = [&]{ result_ = std::move(t); };
- FSM_START
+ FSM_START(fsm_)
case State::Start:
- FSM_UPDATE(State::OnlyResult, setResult_);
+ FSM_UPDATE(fsm_, State::OnlyResult, setResult_);
break;
case State::OnlyCallback:
- FSM_UPDATE(State::Armed, setResult_);
+ FSM_UPDATE(fsm_, State::Armed, setResult_);
transitionToArmed = true;
break;
private:
void maybeCallback() {
- FSM_START
+ FSM_START(fsm_)
case State::Armed:
if (active_) {
- FSM_UPDATE2(State::Done, []{}, std::bind(&Core::doCallback, this));
+ FSM_UPDATE2(fsm_, State::Done, []{},
+ std::bind(&Core::doCallback, this));
}
FSM_BREAK
}
}
- folly::Optional<Try<T>> result_;
- std::function<void(Try<T>&&)> callback_;
- std::shared_ptr<RequestContext> context_{nullptr};
+ FSM<State> fsm_ {State::Start};
std::atomic<unsigned char> detached_ {0};
std::atomic<bool> active_ {true};
- std::atomic<Executor*> executor_ {nullptr};
- exception_wrapper interrupt_;
- std::function<void(exception_wrapper const&)> interruptHandler_;
folly::MicroSpinLock interruptLock_ {0};
+ folly::Optional<Try<T>> result_ {};
+ std::function<void(Try<T>&&)> callback_ {nullptr};
+ std::shared_ptr<RequestContext> context_ {nullptr};
+ std::atomic<Executor*> executor_ {nullptr};
+ exception_wrapper interrupt_ {};
+ std::function<void(exception_wrapper const&)> interruptHandler_ {nullptr};
};
template <typename... Ts>
}
};
-#define FSM_START \
- {bool done = false; while (!done) { auto state = getState(); switch (state) {
+#define FSM_START(fsm) {\
+ bool done = false; \
+ while (!done) { auto state = fsm.getState(); switch (state) {
-#define FSM_UPDATE2(b, protectedAction, unprotectedAction) \
- done = updateState(state, (b), (protectedAction), (unprotectedAction));
+#define FSM_UPDATE2(fsm, b, protectedAction, unprotectedAction) \
+ done = fsm.updateState(state, (b), (protectedAction), (unprotectedAction));
-#define FSM_UPDATE(b, action) FSM_UPDATE2((b), (action), []{})
+#define FSM_UPDATE(fsm, b, action) FSM_UPDATE2(fsm, (b), (action), []{})
-#define FSM_CASE(a, b, action) \
+#define FSM_CASE(fsm, a, b, action) \
case (a): \
- FSM_UPDATE((b), (action)); \
+ FSM_UPDATE(fsm, (b), (action)); \
break;
-#define FSM_CASE2(a, b, protectedAction, unprotectedAction) \
+#define FSM_CASE2(fsm, a, b, protectedAction, unprotectedAction) \
case (a): \
- FSM_UPDATE2((b), (protectedAction), (unprotectedAction)); \
+ FSM_UPDATE2(fsm, (b), (protectedAction), (unprotectedAction)); \
break;
#define FSM_BREAK done = true; break;
}
TEST(FSM, magicMacrosExample) {
- struct MyFSM : public FSM<State> {
+ struct MyFSM {
+ FSM<State> fsm_;
int count = 0;
int unprotectedCount = 0;
- MyFSM() : FSM<State>(State::A) {}
+ MyFSM() : fsm_(State::A) {}
void twiddle() {
- FSM_START
- FSM_CASE(State::A, State::B, [&]{ count++; });
- FSM_CASE2(State::B, State::A,
+ FSM_START(fsm_)
+ FSM_CASE(fsm_, State::A, State::B, [&]{ count++; });
+ FSM_CASE2(fsm_, State::B, State::A,
[&]{ count--; }, [&]{ unprotectedCount--; });
FSM_END
}
MyFSM fsm;
fsm.twiddle();
- EXPECT_EQ(State::B, fsm.getState());
+ EXPECT_EQ(State::B, fsm.fsm_.getState());
EXPECT_EQ(1, fsm.count);
EXPECT_EQ(0, fsm.unprotectedCount);
fsm.twiddle();
- EXPECT_EQ(State::A, fsm.getState());
+ EXPECT_EQ(State::A, fsm.fsm_.getState());
EXPECT_EQ(0, fsm.count);
EXPECT_EQ(-1, fsm.unprotectedCount);
}
typedef FutureException eggs_t;
static eggs_t eggs("eggs");
+// Core
+
+TEST(Future, coreSize) {
+ // If this number goes down, it's fine!
+ // If it goes up, please seek professional advice ;-)
+ size_t size = 168;
+ if (sizeof(folly::exception_wrapper) == 80) {
+ // it contains strings, which can be bigger for -fb builds (e.g. fbstring)
+ size = 200;
+ }
+ EXPECT_EQ(size, sizeof(detail::Core<void>));
+}
+
// Future
TEST(Future, onError) {
folly::Baton<> b;
auto t = std::thread([&]{
b.post();
- std::this_thread::sleep_for(milliseconds(100));
+ /* sleep override */ std::this_thread::sleep_for(milliseconds(100));
p.setValue();
});
b.wait();