2 * Copyright 2014-present Facebook, Inc.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
22 #include <folly/MicroSpinLock.h>
28 /// Finite State Machine helper base class.
29 /// Inherit from this.
30 /// For best results, use an "enum class" for Enum.
34 // I am not templatizing this because folly::MicroSpinLock needs to be
35 // zero-initialized (or call init) which isn't generic enough for something
36 // that behaves like std::mutex. :(
37 using Mutex = folly::MicroSpinLock;
40 // This might not be necessary for all Enum types, e.g. anything
41 // that is atomically updated in practice on this CPU and there's no risk
42 // of returning a bogus state because of tearing.
43 // An optimization would be to use a static conditional on the Enum type.
44 std::atomic<Enum> state_;
47 explicit FSM(Enum startState) : state_(startState) {}
49 Enum getState() const noexcept {
50 return state_.load(std::memory_order_acquire);
53 /// Atomically do a state transition with accompanying action.
54 /// The action will see the old state.
55 /// @returns true on success, false and action unexecuted otherwise
57 bool updateState(Enum A, Enum B, F const& action) {
58 if (!mutex_.try_lock()) {
61 if (state_.load(std::memory_order_acquire) != A) {
66 state_.store(B, std::memory_order_release);
71 /// Atomically do a state transition with accompanying action. Then do the
72 /// unprotected action without holding the lock. If the atomic transition
73 /// fails, returns false and neither action was executed.
75 /// This facilitates code like this:
76 /// bool done = false;
78 /// switch (getState()) {
80 /// done = updateState(State::Foo, State::Bar,
81 /// [&]{ /* do protected stuff */ },
82 /// [&]{ /* do unprotected stuff */});
85 /// Which reads nicer than code like this:
87 /// switch (getState()) {
89 /// if (!updateState(State::Foo, State::Bar,
90 /// [&]{ /* do protected stuff */ })) {
93 /// /* do unprotected stuff */
94 /// return; // or otherwise break out of the loop
96 /// The protected action will see the old state, and the unprotected action
97 /// will see the new state.
98 template <class F1, class F2>
99 bool updateState(Enum A, Enum B,
100 F1 const& protectedAction, F2 const& unprotectedAction) {
101 bool result = updateState(A, B, protectedAction);
109 #define FSM_START(fsm) {\
111 while (!done) { auto state = fsm.getState(); switch (state) {
113 #define FSM_UPDATE2(fsm, b, protectedAction, unprotectedAction) \
114 done = fsm.updateState(state, (b), (protectedAction), (unprotectedAction));
116 #define FSM_UPDATE(fsm, b, action) FSM_UPDATE2(fsm, (b), (action), []{})
118 #define FSM_CASE(fsm, a, b, action) \
120 FSM_UPDATE(fsm, (b), (action)); \
123 #define FSM_CASE2(fsm, a, b, protectedAction, unprotectedAction) \
125 FSM_UPDATE2(fsm, (b), (protectedAction), (unprotectedAction)); \
128 #define FSM_BREAK done = true; break;
131 } // namespace detail
132 } // namespace futures