Commit 7026dbde authored by Marshall Cline's avatar Marshall Cline Committed by Facebook Github Bot

simplify Core state-space

Summary:
Remove Armed: it added an avoidable extra state transition, plus avoidable semantic complexity in the behavior/contract of setResult() & setCallback(), i.e., if either of those transitioned to Armed, that thread may or may not be the one that made the transition to Done with the possibly-synchronous call to the callback.

After this change there is only one transition after Only* - to Done, and the new contract/behavior of setResult/setCallback is still asynchronous (the caller cannot know which thread will call the callback) but at least has only one asynchrony instead of two (the thread that transitions to Done is always the thread that calls doCallback()).

Reviewed By: yfeldblum

Differential Revision: D8049396

fbshipit-source-id: 89a1e40ab8c09db4c8ab0ac3a1fd2cc5e092830d
parent 140a17aa
...@@ -42,20 +42,17 @@ namespace detail { ...@@ -42,20 +42,17 @@ namespace detail {
/* /*
OnlyCallback OnlyCallback
/ \ / \
Start Armed - Done Start Done
\ / \ /
OnlyResult OnlyResult
This state machine is fairly self-explanatory. The most important bit is This state machine is fairly self-explanatory. The most important bit is
that the callback is only executed on the transition from Armed to Done, that the callback is only executed just after the transition from Only* to Done.
and that transition happens immediately after transitioning from Only*
to Armed.
*/ */
enum class State : uint8_t { enum class State : uint8_t {
Start, Start,
OnlyResult, OnlyResult,
OnlyCallback, OnlyCallback,
Armed,
Done, Done,
}; };
...@@ -116,7 +113,6 @@ class Core final { ...@@ -116,7 +113,6 @@ class Core final {
bool hasResult() const noexcept { bool hasResult() const noexcept {
switch (fsm_.getState()) { switch (fsm_.getState()) {
case State::OnlyResult: case State::OnlyResult:
case State::Armed:
case State::Done: case State::Done:
assert(!!result_); assert(!!result_);
return true; return true;
...@@ -142,7 +138,6 @@ class Core final { ...@@ -142,7 +138,6 @@ class Core final {
/// Call only from Future thread. /// Call only from Future thread.
template <typename F> template <typename F>
void setCallback(F&& func) { void setCallback(F&& func) {
bool transitionToArmed = false;
auto setCallback_ = [&]{ auto setCallback_ = [&]{
context_ = RequestContext::saveContext(); context_ = RequestContext::saveContext();
callback_ = std::forward<F>(func); callback_ = std::forward<F>(func);
...@@ -154,28 +149,19 @@ class Core final { ...@@ -154,28 +149,19 @@ class Core final {
return fsm_.tryUpdateState(state, State::OnlyCallback, setCallback_); return fsm_.tryUpdateState(state, State::OnlyCallback, setCallback_);
case State::OnlyResult: case State::OnlyResult:
return fsm_.tryUpdateState(state, State::Armed, setCallback_, [&] { return fsm_.tryUpdateState(
transitionToArmed = true; state, State::Done, setCallback_, [&] { doCallback(); });
});
case State::OnlyCallback: case State::OnlyCallback:
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");
} }
assume_unreachable(); assume_unreachable();
}); });
// we could always call this, it is an optimization to only call it when
// it might be needed.
if (transitionToArmed) {
maybeCallback();
}
} }
/// Call only from Promise thread /// Call only from Promise thread
void setResult(Try<T>&& t) { void setResult(Try<T>&& t) {
bool transitionToArmed = false;
auto setResult_ = [&]{ result_ = std::move(t); }; auto setResult_ = [&]{ result_ = std::move(t); };
fsm_.transition([&](State state) { fsm_.transition([&](State state) {
switch (state) { switch (state) {
...@@ -183,21 +169,15 @@ class Core final { ...@@ -183,21 +169,15 @@ class Core final {
return fsm_.tryUpdateState(state, State::OnlyResult, setResult_); return fsm_.tryUpdateState(state, State::OnlyResult, setResult_);
case State::OnlyCallback: case State::OnlyCallback:
return fsm_.tryUpdateState(state, State::Armed, setResult_, [&] { return fsm_.tryUpdateState(
transitionToArmed = true; state, State::Done, setResult_, [&] { doCallback(); });
});
case State::OnlyResult: case State::OnlyResult:
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");
} }
assume_unreachable(); assume_unreachable();
}); });
if (transitionToArmed) {
maybeCallback();
}
} }
/// Called by a destructing Future (in the Future thread, by definition) /// Called by a destructing Future (in the Future thread, by definition)
...@@ -215,8 +195,7 @@ class Core final { ...@@ -215,8 +195,7 @@ class Core final {
/// the callback has already been invoked, but not concurrently with anything /// the callback has already been invoked, but not concurrently with anything
/// which might trigger invocation of the callback /// which might trigger invocation of the callback
void setExecutor(Executor* x, int8_t priority = Executor::MID_PRI) { void setExecutor(Executor* x, int8_t priority = Executor::MID_PRI) {
auto s = fsm_.getState(); DCHECK(fsm_.getState() != State::OnlyCallback);
DCHECK(s == State::Start || s == State::OnlyResult || s == State::Done);
executor_ = x; executor_ = x;
priority_ = priority; priority_ = priority;
} }
...@@ -314,20 +293,8 @@ class Core final { ...@@ -314,20 +293,8 @@ class Core final {
Core* core_{nullptr}; Core* core_{nullptr};
}; };
void maybeCallback() {
fsm_.transition([&](State state) {
switch (state) {
case State::Armed:
return fsm_.tryUpdateState(
state, State::Done, [] {}, [&] { doCallback(); });
default:
return true;
}
});
}
void doCallback() { void doCallback() {
DCHECK(fsm_.getState() == State::Done);
Executor* x = executor_; Executor* x = executor_;
int8_t priority = priority_; int8_t priority = priority_;
......
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