folly copyright 2015 -> copyright 2016
[folly.git] / folly / futures / detail / FSM.h
1 /*
2  * Copyright 2016 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/MicroSpinLock.h>
22
23 namespace folly { 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_acquire);
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     if (!mutex_.try_lock()) {
56       mutex_.lock();
57     }
58     if (state_.load(std::memory_order_acquire) != A) {
59       mutex_.unlock();
60       return false;
61     }
62     action();
63     state_.store(B, std::memory_order_release);
64     mutex_.unlock();
65     return true;
66   }
67
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.
71   ///
72   /// This facilitates code like this:
73   ///   bool done = false;
74   ///   while (!done) {
75   ///     switch (getState()) {
76   ///     case State::Foo:
77   ///       done = updateState(State::Foo, State::Bar,
78   ///           [&]{ /* do protected stuff */ },
79   ///           [&]{ /* do unprotected stuff */});
80   ///       break;
81   ///
82   /// Which reads nicer than code like this:
83   ///   while (true) {
84   ///     switch (getState()) {
85   ///     case State::Foo:
86   ///       if (!updateState(State::Foo, State::Bar,
87   ///           [&]{ /* do protected stuff */ })) {
88   ///         continue;
89   ///       }
90   ///       /* do unprotected stuff */
91   ///       return; // or otherwise break out of the loop
92   ///
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);
99     if (result) {
100       unprotectedAction();
101     }
102     return result;
103   }
104 };
105
106 #define FSM_START(fsm) {\
107     bool done = false; \
108     while (!done) { auto state = fsm.getState(); switch (state) {
109
110 #define FSM_UPDATE2(fsm, b, protectedAction, unprotectedAction) \
111     done = fsm.updateState(state, (b), (protectedAction), (unprotectedAction));
112
113 #define FSM_UPDATE(fsm, b, action) FSM_UPDATE2(fsm, (b), (action), []{})
114
115 #define FSM_CASE(fsm, a, b, action) \
116   case (a): \
117     FSM_UPDATE(fsm, (b), (action)); \
118     break;
119
120 #define FSM_CASE2(fsm, a, b, protectedAction, unprotectedAction) \
121   case (a): \
122     FSM_UPDATE2(fsm, (b), (protectedAction), (unprotectedAction)); \
123     break;
124
125 #define FSM_BREAK done = true; break;
126 #define FSM_END }}}
127
128
129 }} // folly::detail