codemod: folly/wangle/ -> folly/wangle/futures
[folly.git] / folly / wangle / futures / detail / FSM.h
1 /*
2  * Copyright 2014 Facebook, Inc.
3  *
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
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 #pragma once
18
19 #include <atomic>
20 #include <mutex>
21 #include <folly/SmallLocks.h>
22
23 namespace folly { namespace wangle { namespace detail {
24
25 /// Finite State Machine helper base class.
26 /// Inherit from this.
27 /// For best results, use an "enum class" for Enum.
28 template <class Enum>
29 class FSM {
30 private:
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;
35   Mutex mutex_ {0};
36
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_;
42
43 public:
44   explicit FSM(Enum startState) : state_(startState) {}
45
46   Enum getState() const {
47     return state_.load(std::memory_order_relaxed);
48   }
49
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
53   template <class F>
54   bool updateState(Enum A, Enum B, F const& action) {
55     std::lock_guard<Mutex> lock(mutex_);
56     if (state_ != A) return false;
57     action();
58     state_ = B;
59     return true;
60   }
61
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.
65   ///
66   /// This facilitates code like this:
67   ///   bool done = false;
68   ///   while (!done) {
69   ///     switch (getState()) {
70   ///     case State::Foo:
71   ///       done = updateState(State::Foo, State::Bar,
72   ///           [&]{ /* do protected stuff */ },
73   ///           [&]{ /* do unprotected stuff */});
74   ///       break;
75   ///
76   /// Which reads nicer than code like this:
77   ///   while (true) {
78   ///     switch (getState()) {
79   ///     case State::Foo:
80   ///       if (!updateState(State::Foo, State::Bar,
81   ///           [&]{ /* do protected stuff */ })) {
82   ///         continue;
83   ///       }
84   ///       /* do unprotected stuff */
85   ///       return; // or otherwise break out of the loop
86   ///
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);
93     if (result) {
94       unprotectedAction();
95     }
96     return result;
97   }
98 };
99
100 #define FSM_START \
101   {bool done = false; while (!done) { auto state = getState(); switch (state) {
102
103 #define FSM_UPDATE2(b, protectedAction, unprotectedAction) \
104     done = updateState(state, (b), (protectedAction), (unprotectedAction));
105
106 #define FSM_UPDATE(b, action) FSM_UPDATE2((b), (action), []{})
107
108 #define FSM_CASE(a, b, action) \
109   case (a): \
110     FSM_UPDATE((b), (action)); \
111     break;
112
113 #define FSM_CASE2(a, b, protectedAction, unprotectedAction) \
114   case (a): \
115     FSM_UPDATE2((b), (protectedAction), (unprotectedAction)); \
116     break;
117
118 #define FSM_BREAK done = true; break;
119 #define FSM_END }}}
120
121
122 }}}