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