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