Sort #include lines
[folly.git] / folly / futures / detail / FSM.h
1 /*
2  * Copyright 2017 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
22 #include <folly/MicroSpinLock.h>
23
24 namespace folly { namespace detail {
25
26 /// Finite State Machine helper base class.
27 /// Inherit from this.
28 /// For best results, use an "enum class" for Enum.
29 template <class Enum>
30 class FSM {
31 private:
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;
36   Mutex mutex_ {0};
37
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_;
43
44 public:
45   explicit FSM(Enum startState) : state_(startState) {}
46
47   Enum getState() const noexcept {
48     return state_.load(std::memory_order_acquire);
49   }
50
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
54   template <class F>
55   bool updateState(Enum A, Enum B, F const& action) {
56     if (!mutex_.try_lock()) {
57       mutex_.lock();
58     }
59     if (state_.load(std::memory_order_acquire) != A) {
60       mutex_.unlock();
61       return false;
62     }
63     action();
64     state_.store(B, std::memory_order_release);
65     mutex_.unlock();
66     return true;
67   }
68
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.
72   ///
73   /// This facilitates code like this:
74   ///   bool done = false;
75   ///   while (!done) {
76   ///     switch (getState()) {
77   ///     case State::Foo:
78   ///       done = updateState(State::Foo, State::Bar,
79   ///           [&]{ /* do protected stuff */ },
80   ///           [&]{ /* do unprotected stuff */});
81   ///       break;
82   ///
83   /// Which reads nicer than code like this:
84   ///   while (true) {
85   ///     switch (getState()) {
86   ///     case State::Foo:
87   ///       if (!updateState(State::Foo, State::Bar,
88   ///           [&]{ /* do protected stuff */ })) {
89   ///         continue;
90   ///       }
91   ///       /* do unprotected stuff */
92   ///       return; // or otherwise break out of the loop
93   ///
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);
100     if (result) {
101       unprotectedAction();
102     }
103     return result;
104   }
105 };
106
107 #define FSM_START(fsm) {\
108     bool done = false; \
109     while (!done) { auto state = fsm.getState(); switch (state) {
110
111 #define FSM_UPDATE2(fsm, b, protectedAction, unprotectedAction) \
112     done = fsm.updateState(state, (b), (protectedAction), (unprotectedAction));
113
114 #define FSM_UPDATE(fsm, b, action) FSM_UPDATE2(fsm, (b), (action), []{})
115
116 #define FSM_CASE(fsm, a, b, action) \
117   case (a): \
118     FSM_UPDATE(fsm, (b), (action)); \
119     break;
120
121 #define FSM_CASE2(fsm, a, b, protectedAction, unprotectedAction) \
122   case (a): \
123     FSM_UPDATE2(fsm, (b), (protectedAction), (unprotectedAction)); \
124     break;
125
126 #define FSM_BREAK done = true; break;
127 #define FSM_END }}}
128
129
130 }} // folly::detail