wangle::detail::FSM
[folly.git] / folly / wangle / 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   FSM(Enum startState) : state_(startState) {}
45
46   Enum getState() const {
47     return state_.load(std::memory_order_relaxed);
48   }
49
50   // transition from state A to state B, and then perform action while the
51   // lock is still held.
52   //
53   // If the current state is not A, returns false.
54   template <class F>
55   bool updateState(Enum A, Enum B, F const& action) {
56     std::lock_guard<Mutex> lock(mutex_);
57     if (state_ != A) return false;
58     state_ = B;
59     action();
60     return true;
61   }
62 };
63
64 #define FSM_START \
65   retry: \
66     switch (getState()) {
67
68 #define FSM_UPDATE2(a, b, action, unlocked_code) \
69     case a: \
70       if (!updateState((a), (b), (action))) goto retry; \
71       { unlocked_code ; } \
72       break;
73
74 #define FSM_UPDATE(a, b, action) FSM_UPDATE2((a), (b), (action), {})
75
76 #define FSM_END \
77     }
78
79
80 }}}