Try Contains enum as an enum class
[folly.git] / folly / wangle / ThreadGate.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 #include <memory>
19 #include "Future.h"
20
21 namespace folly { namespace wangle {
22
23 /**
24   Yo dawg, I heard you like asynchrony so I put asynchrony in your asynchronous
25   framework.
26
27   Wangle's futures and promises are not thread safe. Counterintuitive as this
28   may seem at first, this is very intentional. Making futures and promises
29   threadsafe drastically reduces their performance.
30
31   On the other hand, an asynchronous framework isn't much use if you can't do
32   asynchronous things in other threads. So we use the ThreadGate strategy to
33   decouple the threads and their futures with a form of message passing.
34
35   There are two actors, the east thread which does the asynchronous operation
36   (the server) and the west thread that wants the asynchronous operation done
37   (the client).
38
39   The client calls gate<T>(fn), which returns a Future<T>. Practically speaking
40   the returned Future<T> is the same as the Future<T> returned by fn. But
41   there are actually two futures involved - the original Future which will be
42   generated by fn (called the east Future), and the Future actually returned
43   by gate<T>(fn) (called the west Future).
44
45   These two futures are decoupled, and although the fulfilment of the east
46   Future eventually causes fulfilment of the west Future, those fulfilments
47   happen in their own threads.
48
49   In order to make and use a ThreadGate, you need to provide a strategy for
50   executing code in the east and west threads. These strategies may be
51   different. The only requirement is a threadsafe method
52   `void add(function<void()>&&)`. You may find the executors in
53   Executor.h handy, but ensure that you are using them
54   threadsafely.
55
56   In order for your ThreadGate to do anything, you need to drive those
57   executors somehow. An event loop is a natural fit. A thread pool might be
58   made to work. You could use a busy loop to make a very expensive space
59   heater. 0MQ would be pleasant.
60
61   Another pattern supported by the ThreadGate is the single-thread pattern. In
62   this pattern, non-blocking I/O drives the asynchronous operation, and
63   futures are fulfilled in an event loop callback. In this scenario,
64   ThreadGate is largely superfluous, and the executors would likely just
65   execute code immediately and inline (and therefore not need to be driven, or
66   threadsafe). But a Waiter strategy that makes progress by driving the event
67   loop one iteration would allow for gate-and-wait code which is agnostic to
68   the small detail that everything happens in one thread. It would also make
69   Future change toward a multithreaded architecture easier, as you need only
70   change the components of the ThreadGate which your client code is already
71   using.
72   */
73 class ThreadGate {
74 public:
75   virtual ~ThreadGate() {}
76
77   /**
78     Returns a Future that will be fulfilled after the Future that will be
79     returned by fn() has been fulfilled, with the same value or exception
80     (moved).
81
82     There's a lot of nuance in that sentence. Let's break it down.
83
84     fn kicks off the asynchronous operation (makes the east Promise), and must
85     be executed in the east thread because the east thread is where the east
86     Promise will be fulfilled. Since gate is being called from the west
87     thread, we must gate fn using the east executor. fn is not executed
88     immediately, it is queued up and will be executed by the east thread as it
89     drives the executor.
90
91     We create the west Promise and return its Future.
92
93     When the east thread executes its task, fn is called and the resulting
94     Future gets a callback that will gate another task back to the west.
95
96     Sometime later, the asynchronous operation completes and the east Promise
97     is fulfilled. Then the east Future executes its callback, which adds a
98     task to the west executor that task is to fulfil the west Promise with the
99     same Try<T>, and it will execute in the west thread.
100
101     At this point, the west Future is still unfulfilled, even though the east
102     Future has been fulfilled and its callback has finished executing. Only
103     when the west executor is driven to execute that task, the west Future
104     will be completed and its callbacks called.
105
106     In summary, both east and west need to have plans to drive their
107     executors, or nothing will actually happen. When the executors are driven,
108     then everything flows.    */
109   template <class T>
110   Future<T> gate(std::function<Future<T>()>&& fn) {
111     Promise<T> pWest;
112     Future<T> fWest = pWest.getFuture();
113
114     gate(std::move(fn), std::move(pWest));
115     return fWest;
116   }
117
118   /**
119    * This version of gate is to support use cases where the calling thread is
120    * not the west thread. Here is an example use case.
121    *
122    *  Promise<T> pWest;
123    *  Future<T> fWest = pWest.getFuture();
124    *
125    *  // Set up callbacks for west from a thread that is not west.
126    *  fWest.then(...).then(...);
127    *
128    *  threadGate.gate(..., std::move(pWest));
129    *
130    * This function assumes that it is safe to call addEast from a thread that is
131    * not the west thread.
132    */
133   template <class T>
134   void gate(std::function<Future<T>()>&& fn,
135             Promise<T>&& p) {
136     folly::MoveWrapper<Promise<T>> pWest(std::move(p));
137     folly::MoveWrapper<std::function<Future<T>()>> fnm(std::move(fn));
138     this->addEast([pWest, fnm, this]() mutable {
139       (*fnm)().then([pWest, this](Try<T>&& t) mutable {
140         folly::MoveWrapper<Try<T>> tm(std::move(t));
141         this->addWest([pWest, tm]() mutable {
142           pWest->fulfilTry(std::move(*tm));
143         });
144       });
145     });
146   }
147
148   /**
149     If your workflow calls for synchronizing with a
150     west Future, then you may call waitFor, but if your west thread is
151     event-driven you will probably not need to call waitFor.
152
153     In order for waitFor to behave properly, you must ensure that the Waiter's
154     makeProgress method causes some progress to be made on the west thread,
155     i.e. drives the west executor either directly or indirectly.
156
157     (Naturally, progress needs to be made on the east thread as well. i.e. the
158     east executor is driven, the asynchronous operation happens, and its
159     Promise is fulfilled. It is likely that none of this concerns the consumer
160     of waitFor.)
161
162     This is the only function that uses the Waiter. It is never called
163     internally. Therefore, if you never use waitFor you can safely provide a
164     DummyWaiter.
165     */
166   template <class T>
167   void waitFor(Future<T> const& f) {
168     while (!f.isReady()) {
169       this->makeProgress();
170     }
171   }
172
173   template <class T>
174   typename std::add_lvalue_reference<T>::type
175   value(Future<T>& f) {
176     waitFor<T>(f);
177     return f.value();
178   }
179
180   template <class T>
181   typename std::add_lvalue_reference<const T>::type
182   value(Future<T> const& f) {
183     waitFor<T>(f);
184     return f.value();
185   }
186
187   virtual void addEast(std::function<void()>&&) = 0;
188   virtual void addWest(std::function<void()>&&) = 0;
189   virtual void makeProgress();
190 };
191
192 }} // namespace