2 * Copyright 2014 Facebook, Inc.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
24 #include <folly/Optional.h>
26 #include <folly/wangle/Try.h>
27 #include <folly/wangle/Promise.h>
28 #include <folly/wangle/Future.h>
29 #include <folly/wangle/Executor.h>
31 namespace folly { namespace wangle { namespace detail {
33 // As of GCC 4.8.1, the std::function in libstdc++ optimizes only for pointers
34 // to functions, using a helper avoids a call to malloc.
36 void empty_callback(Try<T>&&) { }
38 /** The shared state object for Future and Promise. */
42 // This must be heap-constructed. There's probably a way to enforce that in
43 // code but since this is just internal detail code and I don't know how
44 // off-hand, I'm punting.
48 assert(detached_ == 2);
52 State(State const&) = delete;
53 State& operator=(State const&) = delete;
55 // not movable (see comment in the implementation of Future::then)
56 State(State&&) noexcept = delete;
57 State& operator=(State&&) = delete;
64 void setCallback(F func) {
66 std::lock_guard<decltype(mutex_)> lock(mutex_);
69 throw std::logic_error("setCallback called twice");
72 callback_ = std::move(func);
78 void fulfil(Try<T>&& t) {
80 std::lock_guard<decltype(mutex_)> lock(mutex_);
83 throw std::logic_error("fulfil called twice");
86 value_ = std::move(t);
93 void setException(std::exception_ptr const& e) {
97 template <class E> void setException(E const& e) {
98 fulfil(Try<T>(std::make_exception_ptr<E>(e)));
102 return value_.hasValue();
105 typename std::add_lvalue_reference<T>::type value() {
107 return value_->value();
109 throw FutureNotReady();
113 // Called by a destructing Future
114 void detachFuture() {
116 setCallback(empty_callback<T>);
122 // Called by a destructing Promise
123 void detachPromise() {
125 setException(BrokenPromise());
131 std::lock_guard<decltype(mutex_)> lock(mutex_);
137 std::lock_guard<decltype(mutex_)> lock(mutex_);
143 bool isActive() { return active_; }
145 void setExecutor(Executor* x) {
146 std::lock_guard<decltype(mutex_)> lock(mutex_);
151 void maybeCallback() {
152 std::lock_guard<decltype(mutex_)> lock(mutex_);
154 value_ && callback_ && isActive()) {
155 // TODO(5306911) we should probably try/catch here
157 MoveWrapper<folly::Optional<Try<T>>> val(std::move(value_));
158 MoveWrapper<std::function<void(Try<T>&&)>> cb(std::move(callback_));
159 executor_->add([cb, val]() mutable { (*cb)(std::move(**val)); });
162 callback_(std::move(*value_));
171 std::lock_guard<decltype(mutex_)> lock(mutex_);
173 assert(detached_ == 1 || detached_ == 2);
174 shouldDelete = (detached_ == 2);
178 // we should have already executed the callback with the value
184 folly::Optional<Try<T>> value_;
185 std::function<void(Try<T>&&)> callback_;
186 bool calledBack_ = false;
187 unsigned char detached_ = 0;
189 Executor* executor_ = nullptr;
191 // this lock isn't meant to protect all accesses to members, only the ones
192 // that need to be threadsafe: the act of setting value_ and callback_, and
193 // seeing if they are set and whether we should then continue.
194 std::recursive_mutex mutex_;
197 template <typename... Ts>
198 struct VariadicContext {
199 VariadicContext() : total(0), count(0) {}
200 Promise<std::tuple<Try<Ts>... > > p;
201 std::tuple<Try<Ts>... > results;
203 std::atomic<size_t> count;
204 typedef Future<std::tuple<Try<Ts>...>> type;
207 template <typename... Ts, typename THead, typename... Fs>
208 typename std::enable_if<sizeof...(Fs) == 0, void>::type
209 whenAllVariadicHelper(VariadicContext<Ts...> *ctx, THead&& head, Fs&&... tail) {
210 head.setCallback_([ctx](Try<typename THead::value_type>&& t) {
211 std::get<sizeof...(Ts) - sizeof...(Fs) - 1>(ctx->results) = std::move(t);
212 if (++ctx->count == ctx->total) {
213 ctx->p.setValue(std::move(ctx->results));
219 template <typename... Ts, typename THead, typename... Fs>
220 typename std::enable_if<sizeof...(Fs) != 0, void>::type
221 whenAllVariadicHelper(VariadicContext<Ts...> *ctx, THead&& head, Fs&&... tail) {
222 head.setCallback_([ctx](Try<typename THead::value_type>&& t) {
223 std::get<sizeof...(Ts) - sizeof...(Fs) - 1>(ctx->results) = std::move(t);
224 if (++ctx->count == ctx->total) {
225 ctx->p.setValue(std::move(ctx->results));
229 // template tail-recursion
230 whenAllVariadicHelper(ctx, std::forward<Fs>(tail)...);
233 template <typename T>
234 struct WhenAllContext {
235 explicit WhenAllContext() : count(0), total(0) {}
236 Promise<std::vector<Try<T> > > p;
237 std::vector<Try<T> > results;
238 std::atomic<size_t> count;
242 template <typename T>
243 struct WhenAnyContext {
244 explicit WhenAnyContext(size_t n) : done(false), ref_count(n) {};
245 Promise<std::pair<size_t, Try<T>>> p;
246 std::atomic<bool> done;
247 std::atomic<size_t> ref_count;
249 if (--ref_count == 0) {
255 template <typename T>
256 struct WhenAllLaterContext {
257 explicit WhenAllLaterContext() : count(0), total(0) {}
258 std::function<void(std::vector<Try<T>>&&)> fn;
259 std::vector<Try<T> > results;
260 std::atomic<size_t> count;