2 * Copyright 2017 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.
21 #include <folly/MicroSpinLock.h>
23 namespace folly { namespace detail {
25 /// Finite State Machine helper base class.
26 /// Inherit from this.
27 /// For best results, use an "enum class" for Enum.
31 // I am not templatizing this because folly::MicroSpinLock needs to be
32 // zero-initialized (or call init) which isn't generic enough for something
33 // that behaves like std::mutex. :(
34 using Mutex = folly::MicroSpinLock;
37 // This might not be necessary for all Enum types, e.g. anything
38 // that is atomically updated in practice on this CPU and there's no risk
39 // of returning a bogus state because of tearing.
40 // An optimization would be to use a static conditional on the Enum type.
41 std::atomic<Enum> state_;
44 explicit FSM(Enum startState) : state_(startState) {}
46 Enum getState() const {
47 return state_.load(std::memory_order_acquire);
50 /// Atomically do a state transition with accompanying action.
51 /// The action will see the old state.
52 /// @returns true on success, false and action unexecuted otherwise
54 bool updateState(Enum A, Enum B, F const& action) {
55 if (!mutex_.try_lock()) {
58 if (state_.load(std::memory_order_acquire) != A) {
63 state_.store(B, std::memory_order_release);
68 /// Atomically do a state transition with accompanying action. Then do the
69 /// unprotected action without holding the lock. If the atomic transition
70 /// fails, returns false and neither action was executed.
72 /// This facilitates code like this:
73 /// bool done = false;
75 /// switch (getState()) {
77 /// done = updateState(State::Foo, State::Bar,
78 /// [&]{ /* do protected stuff */ },
79 /// [&]{ /* do unprotected stuff */});
82 /// Which reads nicer than code like this:
84 /// switch (getState()) {
86 /// if (!updateState(State::Foo, State::Bar,
87 /// [&]{ /* do protected stuff */ })) {
90 /// /* do unprotected stuff */
91 /// return; // or otherwise break out of the loop
93 /// The protected action will see the old state, and the unprotected action
94 /// will see the new state.
95 template <class F1, class F2>
96 bool updateState(Enum A, Enum B,
97 F1 const& protectedAction, F2 const& unprotectedAction) {
98 bool result = updateState(A, B, protectedAction);
106 #define FSM_START(fsm) {\
108 while (!done) { auto state = fsm.getState(); switch (state) {
110 #define FSM_UPDATE2(fsm, b, protectedAction, unprotectedAction) \
111 done = fsm.updateState(state, (b), (protectedAction), (unprotectedAction));
113 #define FSM_UPDATE(fsm, b, action) FSM_UPDATE2(fsm, (b), (action), []{})
115 #define FSM_CASE(fsm, a, b, action) \
117 FSM_UPDATE(fsm, (b), (action)); \
120 #define FSM_CASE2(fsm, a, b, protectedAction, unprotectedAction) \
122 FSM_UPDATE2(fsm, (b), (protectedAction), (unprotectedAction)); \
125 #define FSM_BREAK done = true; break;