2 * Copyright 2014 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/SmallLocks.h>
23 namespace folly { namespace wangle { 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_relaxed);
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 std::lock_guard<Mutex> lock(mutex_);
56 if (state_ != A) return false;
62 /// Atomically do a state transition with accompanying action. Then do the
63 /// unprotected action without holding the lock. If the atomic transition
64 /// fails, returns false and neither action was executed.
66 /// This facilitates code like this:
67 /// bool done = false;
69 /// switch (getState()) {
71 /// done = updateState(State::Foo, State::Bar,
72 /// [&]{ /* do protected stuff */ },
73 /// [&]{ /* do unprotected stuff */});
76 /// Which reads nicer than code like this:
78 /// switch (getState()) {
80 /// if (!updateState(State::Foo, State::Bar,
81 /// [&]{ /* do protected stuff */ })) {
84 /// /* do unprotected stuff */
85 /// return; // or otherwise break out of the loop
87 /// The protected action will see the old state, and the unprotected action
88 /// will see the new state.
89 template <class F1, class F2>
90 bool updateState(Enum A, Enum B,
91 F1 const& protectedAction, F2 const& unprotectedAction) {
92 bool result = updateState(A, B, protectedAction);
101 {bool done = false; while (!done) { auto state = getState(); switch (state) {
103 #define FSM_UPDATE2(b, protectedAction, unprotectedAction) \
104 done = updateState(state, (b), (protectedAction), (unprotectedAction));
106 #define FSM_UPDATE(b, action) FSM_UPDATE2((b), (action), []{})
108 #define FSM_CASE(a, b, action) \
110 FSM_UPDATE((b), (action)); \
113 #define FSM_CASE2(a, b, protectedAction, unprotectedAction) \
115 FSM_UPDATE2((b), (protectedAction), (unprotectedAction)); \
118 #define FSM_BREAK done = true; break;