Commit 5604a147 authored by Yedidya Feldblum's avatar Yedidya Feldblum Committed by Facebook Github Bot

Avoid magic macros in Futures FSM

Summary: [Folly] Avoid magic macros in Futures `FSM` - just use normal C++ types and functions instead.

Reviewed By: LeeHowes

Differential Revision: D7725582

fbshipit-source-id: 822e4ca6391a37dc4007833c975fe283cfe5105e
parent caf8bd4a
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#include <folly/Utility.h> #include <folly/Utility.h>
#include <folly/futures/FutureException.h> #include <folly/futures/FutureException.h>
#include <folly/futures/detail/FSM.h> #include <folly/futures/detail/FSM.h>
#include <folly/lang/Assume.h>
#include <folly/lang/Exception.h> #include <folly/lang/Exception.h>
#include <folly/synchronization/MicroSpinLock.h> #include <folly/synchronization/MicroSpinLock.h>
...@@ -161,21 +162,23 @@ class Core final { ...@@ -161,21 +162,23 @@ class Core final {
callback_ = std::forward<F>(func); callback_ = std::forward<F>(func);
}; };
FSM_START(fsm_) fsm_.transition([&](State state) {
switch (state) {
case State::Start: case State::Start:
FSM_UPDATE(fsm_, State::OnlyCallback, setCallback_); return fsm_.tryUpdateState(state, State::OnlyCallback, setCallback_);
break;
case State::OnlyResult: case State::OnlyResult:
FSM_UPDATE(fsm_, State::Armed, setCallback_); return fsm_.tryUpdateState(state, State::Armed, setCallback_, [&] {
transitionToArmed = true; transitionToArmed = true;
break; });
case State::OnlyCallback: case State::OnlyCallback:
case State::Armed: case State::Armed:
case State::Done: case State::Done:
throw_exception<std::logic_error>("setCallback called twice"); throw_exception<std::logic_error>("setCallback called twice");
FSM_END }
assume_unreachable();
});
// we could always call this, it is an optimization to only call it when // we could always call this, it is an optimization to only call it when
// it might be needed. // it might be needed.
...@@ -188,21 +191,23 @@ class Core final { ...@@ -188,21 +191,23 @@ class Core final {
void setResult(Try<T>&& t) { void setResult(Try<T>&& t) {
bool transitionToArmed = false; bool transitionToArmed = false;
auto setResult_ = [&]{ result_ = std::move(t); }; auto setResult_ = [&]{ result_ = std::move(t); };
FSM_START(fsm_) fsm_.transition([&](State state) {
switch (state) {
case State::Start: case State::Start:
FSM_UPDATE(fsm_, State::OnlyResult, setResult_); return fsm_.tryUpdateState(state, State::OnlyResult, setResult_);
break;
case State::OnlyCallback: case State::OnlyCallback:
FSM_UPDATE(fsm_, State::Armed, setResult_); return fsm_.tryUpdateState(state, State::Armed, setResult_, [&] {
transitionToArmed = true; transitionToArmed = true;
break; });
case State::OnlyResult: case State::OnlyResult:
case State::Armed: case State::Armed:
case State::Done: case State::Done:
throw_exception<std::logic_error>("setResult called twice"); throw_exception<std::logic_error>("setResult called twice");
FSM_END }
assume_unreachable();
});
if (transitionToArmed) { if (transitionToArmed) {
maybeCallback(); maybeCallback();
...@@ -321,16 +326,19 @@ class Core final { ...@@ -321,16 +326,19 @@ class Core final {
}; };
void maybeCallback() { void maybeCallback() {
FSM_START(fsm_) fsm_.transition([&](State state) {
switch (state) {
case State::Armed: case State::Armed:
if (active_.load(std::memory_order_acquire)) { if (active_.load(std::memory_order_acquire)) {
FSM_UPDATE2(fsm_, State::Done, []{}, [this]{ this->doCallback(); }); return fsm_.tryUpdateState(
state, State::Done, [] {}, [&] { doCallback(); });
} }
FSM_BREAK return true;
default: default:
FSM_BREAK return true;
FSM_END }
});
} }
void doCallback() { void doCallback() {
......
...@@ -99,28 +99,13 @@ class FSM { ...@@ -99,28 +99,13 @@ class FSM {
} }
return result; return result;
} }
};
#define FSM_START(fsm) {\
bool done = false; \
while (!done) { auto state = fsm.getState(); switch (state) {
#define FSM_UPDATE2(fsm, b, protectedAction, unprotectedAction) \
done = fsm.tryUpdateState(state, (b), (protectedAction), (unprotectedAction));
#define FSM_UPDATE(fsm, b, action) FSM_UPDATE2(fsm, (b), (action), []{})
#define FSM_CASE(fsm, a, b, action) \
case (a): \
FSM_UPDATE(fsm, (b), (action)); \
break;
#define FSM_CASE2(fsm, a, b, protectedAction, unprotectedAction) \
case (a): \
FSM_UPDATE2(fsm, (b), (protectedAction), (unprotectedAction)); \
break;
#define FSM_BREAK done = true; break; template <class F>
#define FSM_END }}} void transition(F f) {
while (!f(getState())) {
}
}
};
} // namespace detail } // namespace detail
} // namespace futures } // namespace futures
......
...@@ -15,6 +15,8 @@ ...@@ -15,6 +15,8 @@
*/ */
#include <folly/futures/detail/FSM.h> #include <folly/futures/detail/FSM.h>
#include <folly/lang/Assume.h>
#include <folly/portability/GTest.h> #include <folly/portability/GTest.h>
using namespace folly::futures::detail; using namespace folly::futures::detail;
...@@ -35,7 +37,7 @@ TEST(FSM, example) { ...@@ -35,7 +37,7 @@ TEST(FSM, example) {
return fsm.tryUpdateState( return fsm.tryUpdateState(
State::B, State::A, [&] { count--; }, [&] { unprotectedCount--; }); State::B, State::A, [&] { count--; }, [&] { unprotectedCount--; });
} }
return false; // unreachable folly::assume_unreachable();
}; };
// keep retrying until success (like a cas) // keep retrying until success (like a cas)
...@@ -54,18 +56,23 @@ TEST(FSM, example) { ...@@ -54,18 +56,23 @@ TEST(FSM, example) {
EXPECT_EQ(-1, unprotectedCount); EXPECT_EQ(-1, unprotectedCount);
} }
TEST(FSM, magicMacrosExample) { TEST(FSM, transition) {
struct MyFSM { struct MyFSM {
FSM<State, std::mutex> fsm_; FSM<State, std::mutex> fsm_;
int count = 0; int count = 0;
int unprotectedCount = 0; int unprotectedCount = 0;
MyFSM() : fsm_(State::A) {} MyFSM() : fsm_(State::A) {}
void twiddle() { void twiddle() {
FSM_START(fsm_) fsm_.transition([&](State state) {
FSM_CASE(fsm_, State::A, State::B, [&]{ count++; }); switch (state) {
FSM_CASE2(fsm_, State::B, State::A, case State::A:
[&]{ count--; }, [&]{ unprotectedCount--; }); return fsm_.tryUpdateState(state, State::B, [&] { count++; });
FSM_END case State::B:
return fsm_.tryUpdateState(
state, State::A, [&] { count--; }, [&] { unprotectedCount--; });
}
folly::assume_unreachable();
});
} }
}; };
......
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