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>
25 #include <folly/SmallLocks.h>
27 #include <folly/wangle/Try.h>
28 #include <folly/wangle/Promise.h>
29 #include <folly/wangle/Future.h>
30 #include <folly/wangle/Executor.h>
32 namespace folly { namespace wangle { namespace detail {
34 // As of GCC 4.8.1, the std::function in libstdc++ optimizes only for pointers
35 // to functions, using a helper avoids a call to malloc.
37 void empty_callback(Try<T>&&) { }
39 /** The shared state object for Future and Promise. */
43 // This must be heap-constructed. There's probably a way to enforce that in
44 // code but since this is just internal detail code and I don't know how
45 // off-hand, I'm punting.
49 assert(detached_ == 2);
53 Core(Core const&) = delete;
54 Core& operator=(Core const&) = delete;
56 // not movable (see comment in the implementation of Future::then)
57 Core(Core&&) noexcept = delete;
58 Core& operator=(Core&&) = delete;
65 void setCallback(F func) {
67 std::lock_guard<decltype(mutex_)> lock(mutex_);
70 throw std::logic_error("setCallback called twice");
73 callback_ = std::move(func);
79 void fulfil(Try<T>&& t) {
81 std::lock_guard<decltype(mutex_)> lock(mutex_);
84 throw std::logic_error("fulfil called twice");
87 value_ = std::move(t);
94 void setException(std::exception_ptr const& e) {
98 template <class E> void setException(E const& e) {
99 fulfil(Try<T>(std::make_exception_ptr<E>(e)));
103 return value_.hasValue();
106 typename std::add_lvalue_reference<T>::type value() {
108 return value_->value();
110 throw FutureNotReady();
114 // Called by a destructing Future
115 void detachFuture() {
117 setCallback(empty_callback<T>);
123 // Called by a destructing Promise
124 void detachPromise() {
126 setException(BrokenPromise());
132 std::lock_guard<decltype(mutex_)> lock(mutex_);
138 std::lock_guard<decltype(mutex_)> lock(mutex_);
144 bool isActive() { return active_; }
146 void setExecutor(Executor* x) {
147 std::lock_guard<decltype(mutex_)> lock(mutex_);
152 void maybeCallback() {
153 std::unique_lock<decltype(mutex_)> lock(mutex_);
155 value_ && callback_ && isActive()) {
156 // TODO(5306911) we should probably try/catch here
158 MoveWrapper<folly::Optional<Try<T>>> val(std::move(value_));
159 MoveWrapper<std::function<void(Try<T>&&)>> cb(std::move(callback_));
160 executor_->add([cb, val]() mutable { (*cb)(std::move(**val)); });
165 callback_(std::move(*value_));
173 std::lock_guard<decltype(mutex_)> lock(mutex_);
175 assert(detached_ == 1 || detached_ == 2);
176 shouldDelete = (detached_ == 2);
180 // we should have already executed the callback with the value
186 folly::Optional<Try<T>> value_;
187 std::function<void(Try<T>&&)> callback_;
188 bool calledBack_ = false;
189 unsigned char detached_ = 0;
191 Executor* executor_ = nullptr;
193 // this lock isn't meant to protect all accesses to members, only the ones
194 // that need to be threadsafe: the act of setting value_ and callback_, and
195 // seeing if they are set and whether we should then continue.
196 folly::MicroSpinLock mutex_ {0};
199 template <typename... Ts>
200 struct VariadicContext {
201 VariadicContext() : total(0), count(0) {}
202 Promise<std::tuple<Try<Ts>... > > p;
203 std::tuple<Try<Ts>... > results;
205 std::atomic<size_t> count;
206 typedef Future<std::tuple<Try<Ts>...>> type;
209 template <typename... Ts, typename THead, typename... Fs>
210 typename std::enable_if<sizeof...(Fs) == 0, void>::type
211 whenAllVariadicHelper(VariadicContext<Ts...> *ctx, THead&& head, Fs&&... tail) {
212 head.setCallback_([ctx](Try<typename THead::value_type>&& t) {
213 std::get<sizeof...(Ts) - sizeof...(Fs) - 1>(ctx->results) = std::move(t);
214 if (++ctx->count == ctx->total) {
215 ctx->p.setValue(std::move(ctx->results));
221 template <typename... Ts, typename THead, typename... Fs>
222 typename std::enable_if<sizeof...(Fs) != 0, void>::type
223 whenAllVariadicHelper(VariadicContext<Ts...> *ctx, THead&& head, Fs&&... tail) {
224 head.setCallback_([ctx](Try<typename THead::value_type>&& t) {
225 std::get<sizeof...(Ts) - sizeof...(Fs) - 1>(ctx->results) = std::move(t);
226 if (++ctx->count == ctx->total) {
227 ctx->p.setValue(std::move(ctx->results));
231 // template tail-recursion
232 whenAllVariadicHelper(ctx, std::forward<Fs>(tail)...);
235 template <typename T>
236 struct WhenAllContext {
237 explicit WhenAllContext() : count(0), total(0) {}
238 Promise<std::vector<Try<T> > > p;
239 std::vector<Try<T> > results;
240 std::atomic<size_t> count;
244 template <typename T>
245 struct WhenAnyContext {
246 explicit WhenAnyContext(size_t n) : done(false), ref_count(n) {};
247 Promise<std::pair<size_t, Try<T>>> p;
248 std::atomic<bool> done;
249 std::atomic<size_t> ref_count;
251 if (--ref_count == 0) {
257 template <typename T>
258 struct WhenAllLaterContext {
259 explicit WhenAllLaterContext() : count(0), total(0) {}
260 std::function<void(std::vector<Try<T>>&&)> fn;
261 std::vector<Try<T> > results;
262 std::atomic<size_t> count;