(wangle) more comment tweaks
[folly.git] / folly / wangle / Later.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 "folly/wangle/Executor.h"
20 #include "folly/wangle/Future.h"
21 #include "folly/Optional.h"
22
23 namespace folly { namespace wangle {
24
25 template <typename T> struct isLaterOrFuture;
26 template <typename T> struct isLater;
27
28 /*
29  * Since wangle primitives (promise/future) are not thread safe, it is difficult
30  * to build complex asynchronous workflows. A Later allows you to build such a
31  * workflow before actually launching it so that continuations can be set in a
32  * threadsafe manner.
33  *
34  * The interface to add additional work is the same as future: a then() method
35  * that can take either a type T, a Future<T>, or a Later<T>
36  *
37  * Thread transitions are done by using executors and calling the via() method.
38  *
39  * Here is an example of a workflow:
40  *
41  * Later<ClientRequest> later(std::move(request));
42  *
43  * auto future = later.
44  *   .via(cpuExecutor)
45  *   .then([=](Try<ClientRequest>&& t) { return doCpuWork(t.value()); })
46  *   .via(diskExecutor)
47  *   .then([=](Try<CpuResponse>&& t) { return doDiskWork(t.value()); })
48  *   .via(serverExecutor)
49  *   .then([=]Try<DiskResponse>&& t) { return sendClientResponse(t.value()); })
50  *   .launch();
51  *
52  * Although this workflow traverses many threads, we are able to string
53  * continuations together in a threadsafe manner.
54  *
55  * Laters can also be used to wrap preexisting asynchronous modules that were
56  * not built with wangle in mind. You can create a Later with a function that
57  * takes a callback as input. The function will not actually be called until
58  * launch(), allowing you to string then() statements on top of the callback.
59  */
60 template <class T>
61 class Later {
62  public:
63   typedef T value_type;
64
65   template <class U = void,
66             class = typename std::enable_if<std::is_void<U>::value>::type,
67             class = typename std::enable_if<std::is_same<T, U>::value>::type>
68   Later();
69
70   template <class U,
71             class = typename std::enable_if<!std::is_void<U>::value>::type,
72             class = typename std::enable_if<std::is_same<T, U>::value>::type>
73   explicit Later(U&& input);
74
75   /*
76    * then() adds additional work to the end of the workflow. If the lambda
77    * provided to then() returns a future, that future must be fulfilled in the
78    * same thread of the last set executor (either at constructor or from a call
79    * to via()).
80    */
81   template <class F>
82   typename std::enable_if<
83     !isLaterOrFuture<typename std::result_of<F(Try<T>&&)>::type>::value,
84     Later<typename std::result_of<F(Try<T>&&)>::type> >::type
85   then(F&& fn);
86
87   template <class F>
88   typename std::enable_if<
89     isFuture<typename std::result_of<F(Try<T>&&)>::type>::value,
90     Later<typename std::result_of<F(Try<T>&&)>::type::value_type> >::type
91   then(F&& fn);
92
93   /*
94    * If the function passed to then() returns a Later<T>, calls to then() will
95    * be chained to the new Later before launching the new Later.
96    *
97    * This can be used to build asynchronous modules that can be called from a
98    * user thread and completed in a callback thread. Callbacks can be set up
99    * ahead of time without thread safety issues.
100    *
101    * Using the Later(std::function<void(std::function<void(T&&)>)>&& fn)
102    * constructor, you can wrap existing asynchronous modules with a Later and
103    * can chain it to wangle asynchronous workflows via this call.
104    */
105   template <class F>
106   typename std::enable_if<
107     isLater<typename std::result_of<F(Try<T>&&)>::type>::value,
108     Later<typename std::result_of<F(Try<T>&&)>::type::value_type> >::type
109   then(F&& fn);
110
111   /*
112    * Resets the executor - all then() calls made after the call to via() will be
113    * made in the new executor. The Executor must outlive.
114    */
115   Later<T> via(Executor* executor);
116
117   /*
118    * Starts the workflow. The function provided in the constructor will be
119    * called in the executor provided in the constructor. Subsequent then()
120    * calls will be made, potentially changing threads if a via() call is made.
121    * The future returned will be fulfilled in the last executor.
122    *
123    * Thread safety issues of Futures still apply. If you want to wait on the
124    * Future, it must be done in the thread that will fulfil it. If you do not
125    * plan to use the result of the Future, use fireAndForget()
126    */
127   Future<T> launch();
128
129   /*
130    * Same as launch, only no Future is returned. This guarantees thread safe
131    * cleanup of the internal Futures, even if the Later completes in a different
132    * thread than the thread that calls fireAndForget().
133    */
134   void fireAndForget();
135
136  private:
137   Promise<void> starter_;
138   folly::Optional<Future<T>> future_;
139
140   struct hide { };
141
142   explicit Later(Promise<void>&& starter);
143
144   template <class U>
145   friend class Later;
146 };
147
148 }}
149
150 #include "Later-inl.h"