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.
22 #include <folly/MicroSpinLock.h>
24 namespace folly { namespace detail {
26 /// Finite State Machine helper base class.
27 /// Inherit from this.
28 /// For best results, use an "enum class" for Enum.
32 // I am not templatizing this because folly::MicroSpinLock needs to be
33 // zero-initialized (or call init) which isn't generic enough for something
34 // that behaves like std::mutex. :(
35 using Mutex = folly::MicroSpinLock;
38 // This might not be necessary for all Enum types, e.g. anything
39 // that is atomically updated in practice on this CPU and there's no risk
40 // of returning a bogus state because of tearing.
41 // An optimization would be to use a static conditional on the Enum type.
42 std::atomic<Enum> state_;
45 explicit FSM(Enum startState) : state_(startState) {}
47 Enum getState() const noexcept {
48 return state_.load(std::memory_order_acquire);
51 /// Atomically do a state transition with accompanying action.
52 /// The action will see the old state.
53 /// @returns true on success, false and action unexecuted otherwise
55 bool updateState(Enum A, Enum B, F const& action) {
56 if (!mutex_.try_lock()) {
59 if (state_.load(std::memory_order_acquire) != A) {
64 state_.store(B, std::memory_order_release);
69 /// Atomically do a state transition with accompanying action. Then do the
70 /// unprotected action without holding the lock. If the atomic transition
71 /// fails, returns false and neither action was executed.
73 /// This facilitates code like this:
74 /// bool done = false;
76 /// switch (getState()) {
78 /// done = updateState(State::Foo, State::Bar,
79 /// [&]{ /* do protected stuff */ },
80 /// [&]{ /* do unprotected stuff */});
83 /// Which reads nicer than code like this:
85 /// switch (getState()) {
87 /// if (!updateState(State::Foo, State::Bar,
88 /// [&]{ /* do protected stuff */ })) {
91 /// /* do unprotected stuff */
92 /// return; // or otherwise break out of the loop
94 /// The protected action will see the old state, and the unprotected action
95 /// will see the new state.
96 template <class F1, class F2>
97 bool updateState(Enum A, Enum B,
98 F1 const& protectedAction, F2 const& unprotectedAction) {
99 bool result = updateState(A, B, protectedAction);
107 #define FSM_START(fsm) {\
109 while (!done) { auto state = fsm.getState(); switch (state) {
111 #define FSM_UPDATE2(fsm, b, protectedAction, unprotectedAction) \
112 done = fsm.updateState(state, (b), (protectedAction), (unprotectedAction));
114 #define FSM_UPDATE(fsm, b, action) FSM_UPDATE2(fsm, (b), (action), []{})
116 #define FSM_CASE(fsm, a, b, action) \
118 FSM_UPDATE(fsm, (b), (action)); \
121 #define FSM_CASE2(fsm, a, b, protectedAction, unprotectedAction) \
123 FSM_UPDATE2(fsm, (b), (protectedAction), (unprotectedAction)); \
126 #define FSM_BREAK done = true; break;