2 * Copyright 2015 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.
22 #include <folly/experimental/fibers/Baton.h>
23 #include <folly/Optional.h>
24 #include <folly/futures/detail/Core.h>
25 #include <folly/futures/Timekeeper.h>
32 Timekeeper* getTimekeeperSingleton();
36 Future<T>::Future(Future<T>&& other) noexcept : core_(other.core_) {
37 other.core_ = nullptr;
41 Future<T>& Future<T>::operator=(Future<T>&& other) noexcept {
42 std::swap(core_, other.core_);
47 template <class T2, typename>
48 Future<T>::Future(T2&& val) : core_(nullptr) {
50 p.setValue(std::forward<T2>(val));
51 *this = p.getFuture();
56 typename std::enable_if<
57 folly::is_void_or_unit<T2>::value,
59 Future<T>::Future() : core_(nullptr) {
62 *this = p.getFuture();
67 Future<T>::~Future() {
72 void Future<T>::detach() {
74 core_->detachFuture();
80 void Future<T>::throwIfInvalid() const {
87 void Future<T>::setCallback_(F&& func) {
89 core_->setCallback(std::move(func));
96 typename std::enable_if<isFuture<F>::value,
97 Future<typename isFuture<T>::Inner>>::type
99 return then([](Future<typename isFuture<T>::Inner> internal_future) {
100 return internal_future;
106 // Variant: returns a value
107 // e.g. f.then([](Try<T>&& t){ return t.value(); });
109 template <typename F, typename R, bool isTry, typename... Args>
110 typename std::enable_if<!R::ReturnsFuture::value, typename R::Return>::type
111 Future<T>::thenImplementation(F func, detail::argResult<isTry, F, Args...>) {
112 static_assert(sizeof...(Args) <= 1, "Then must take zero/one argument");
113 typedef typename R::ReturnsFuture::Inner B;
117 // wrap these so we can move them into the lambda
118 folly::MoveWrapper<Promise<B>> p;
119 folly::MoveWrapper<F> funcm(std::forward<F>(func));
121 // grab the Future now before we lose our handle on the Promise
122 auto f = p->getFuture();
124 f.setExecutor(getExecutor());
127 /* This is a bit tricky.
129 We can't just close over *this in case this Future gets moved. So we
130 make a new dummy Future. We could figure out something more
131 sophisticated that avoids making a new Future object when it can, as an
132 optimization. But this is correct.
134 core_ can't be moved, it is explicitly disallowed (as is copying). But
135 if there's ever a reason to allow it, this is one place that makes that
136 assumption and would need to be fixed. We use a standard shared pointer
137 for core_ (by copying it in), which means in essence obj holds a shared
138 pointer to itself. But this shouldn't leak because Promise will not
139 outlive the continuation, because Promise will setException() with a
140 broken Promise if it is destructed before completed. We could use a
141 weak pointer but it would have to be converted to a shared pointer when
142 func is executed (because the Future returned by func may possibly
143 persist beyond the callback, if it gets moved), and so it is an
144 optimization to just make it shared from the get-go.
146 We have to move in the Promise and func using the MoveWrapper
147 hack. (func could be copied but it's a big drag on perf).
149 Two subtle but important points about this design. detail::Core has no
150 back pointers to Future or Promise, so if Future or Promise get moved
151 (and they will be moved in performant code) we don't have to do
152 anything fancy. And because we store the continuation in the
153 detail::Core, not in the Future, we can execute the continuation even
154 after the Future has gone out of scope. This is an intentional design
155 decision. It is likely we will want to be able to cancel a continuation
156 in some circumstances, but I think it should be explicit not implicit
157 in the destruction of the Future used to create it.
160 [p, funcm](Try<T>&& t) mutable {
161 if (!isTry && t.hasException()) {
162 p->setException(std::move(t.exception()));
165 return (*funcm)(t.template get<isTry, Args>()...);
173 // Variant: returns a Future
174 // e.g. f.then([](T&& t){ return makeFuture<T>(t); });
176 template <typename F, typename R, bool isTry, typename... Args>
177 typename std::enable_if<R::ReturnsFuture::value, typename R::Return>::type
178 Future<T>::thenImplementation(F func, detail::argResult<isTry, F, Args...>) {
179 static_assert(sizeof...(Args) <= 1, "Then must take zero/one argument");
180 typedef typename R::ReturnsFuture::Inner B;
184 // wrap these so we can move them into the lambda
185 folly::MoveWrapper<Promise<B>> p;
186 folly::MoveWrapper<F> funcm(std::forward<F>(func));
188 // grab the Future now before we lose our handle on the Promise
189 auto f = p->getFuture();
191 f.setExecutor(getExecutor());
195 [p, funcm](Try<T>&& t) mutable {
196 if (!isTry && t.hasException()) {
197 p->setException(std::move(t.exception()));
200 auto f2 = (*funcm)(t.template get<isTry, Args>()...);
201 // that didn't throw, now we can steal p
202 f2.setCallback_([p](Try<B>&& b) mutable {
203 p->setTry(std::move(b));
205 } catch (const std::exception& e) {
206 p->setException(exception_wrapper(std::current_exception(), e));
208 p->setException(exception_wrapper(std::current_exception()));
216 template <typename T>
217 template <typename R, typename Caller, typename... Args>
218 Future<typename isFuture<R>::Inner>
219 Future<T>::then(R(Caller::*func)(Args...), Caller *instance) {
220 typedef typename std::remove_cv<
221 typename std::remove_reference<
222 typename detail::ArgType<Args...>::FirstArg>::type>::type FirstArg;
223 return then([instance, func](Try<T>&& t){
224 return (instance->*func)(t.template get<isTry<FirstArg>::value, Args>()...);
229 template <class Executor, class Arg, class... Args>
230 auto Future<T>::then(Executor* x, Arg&& arg, Args&&... args)
231 -> decltype(this->then(std::forward<Arg>(arg),
232 std::forward<Args>(args)...))
234 auto oldX = getExecutor();
236 return this->then(std::forward<Arg>(arg), std::forward<Args>(args)...).
241 Future<void> Future<T>::then() {
242 return then([] (Try<T>&& t) {});
245 // onError where the callback returns T
248 typename std::enable_if<
249 !detail::callableWith<F, exception_wrapper>::value &&
250 !detail::Extract<F>::ReturnsFuture::value,
252 Future<T>::onError(F&& func) {
253 typedef typename detail::Extract<F>::FirstArg Exn;
255 std::is_same<typename detail::Extract<F>::RawReturn, T>::value,
256 "Return type of onError callback must be T or Future<T>");
259 auto f = p.getFuture();
260 auto pm = folly::makeMoveWrapper(std::move(p));
261 auto funcm = folly::makeMoveWrapper(std::move(func));
262 setCallback_([pm, funcm](Try<T>&& t) mutable {
263 if (!t.template withException<Exn>([&] (Exn& e) {
268 pm->setTry(std::move(t));
275 // onError where the callback returns Future<T>
278 typename std::enable_if<
279 !detail::callableWith<F, exception_wrapper>::value &&
280 detail::Extract<F>::ReturnsFuture::value,
282 Future<T>::onError(F&& func) {
284 std::is_same<typename detail::Extract<F>::Return, Future<T>>::value,
285 "Return type of onError callback must be T or Future<T>");
286 typedef typename detail::Extract<F>::FirstArg Exn;
289 auto f = p.getFuture();
290 auto pm = folly::makeMoveWrapper(std::move(p));
291 auto funcm = folly::makeMoveWrapper(std::move(func));
292 setCallback_([pm, funcm](Try<T>&& t) mutable {
293 if (!t.template withException<Exn>([&] (Exn& e) {
295 auto f2 = (*funcm)(e);
296 f2.setCallback_([pm](Try<T>&& t2) mutable {
297 pm->setTry(std::move(t2));
299 } catch (const std::exception& e2) {
300 pm->setException(exception_wrapper(std::current_exception(), e2));
302 pm->setException(exception_wrapper(std::current_exception()));
305 pm->setTry(std::move(t));
314 Future<T> Future<T>::ensure(F func) {
315 MoveWrapper<F> funcw(std::move(func));
316 return this->then([funcw](Try<T>&& t) {
318 return makeFuture(std::move(t));
324 Future<T> Future<T>::onTimeout(Duration dur, F&& func, Timekeeper* tk) {
325 auto funcw = folly::makeMoveWrapper(std::forward<F>(func));
326 return within(dur, tk)
327 .onError([funcw](TimedOut const&) { return (*funcw)(); });
332 typename std::enable_if<
333 detail::callableWith<F, exception_wrapper>::value &&
334 detail::Extract<F>::ReturnsFuture::value,
336 Future<T>::onError(F&& func) {
338 std::is_same<typename detail::Extract<F>::Return, Future<T>>::value,
339 "Return type of onError callback must be T or Future<T>");
342 auto f = p.getFuture();
343 auto pm = folly::makeMoveWrapper(std::move(p));
344 auto funcm = folly::makeMoveWrapper(std::move(func));
345 setCallback_([pm, funcm](Try<T> t) mutable {
346 if (t.hasException()) {
348 auto f2 = (*funcm)(std::move(t.exception()));
349 f2.setCallback_([pm](Try<T> t2) mutable {
350 pm->setTry(std::move(t2));
352 } catch (const std::exception& e2) {
353 pm->setException(exception_wrapper(std::current_exception(), e2));
355 pm->setException(exception_wrapper(std::current_exception()));
358 pm->setTry(std::move(t));
365 // onError(exception_wrapper) that returns T
368 typename std::enable_if<
369 detail::callableWith<F, exception_wrapper>::value &&
370 !detail::Extract<F>::ReturnsFuture::value,
372 Future<T>::onError(F&& func) {
374 std::is_same<typename detail::Extract<F>::Return, Future<T>>::value,
375 "Return type of onError callback must be T or Future<T>");
378 auto f = p.getFuture();
379 auto pm = folly::makeMoveWrapper(std::move(p));
380 auto funcm = folly::makeMoveWrapper(std::move(func));
381 setCallback_([pm, funcm](Try<T> t) mutable {
382 if (t.hasException()) {
384 return (*funcm)(std::move(t.exception()));
387 pm->setTry(std::move(t));
395 typename std::add_lvalue_reference<T>::type Future<T>::value() {
398 return core_->getTry().value();
402 typename std::add_lvalue_reference<const T>::type Future<T>::value() const {
405 return core_->getTry().value();
409 Try<T>& Future<T>::getTry() {
412 return core_->getTry();
416 Optional<Try<T>> Future<T>::poll() {
418 if (core_->ready()) {
419 o = std::move(core_->getTry());
425 inline Future<T> Future<T>::via(Executor* executor, int8_t priority) && {
428 setExecutor(executor, priority);
430 return std::move(*this);
434 inline Future<T> Future<T>::via(Executor* executor, int8_t priority) & {
437 MoveWrapper<Promise<T>> p;
438 auto f = p->getFuture();
439 then([p](Try<T>&& t) mutable { p->setTry(std::move(t)); });
440 return std::move(f).via(executor, priority);
444 bool Future<T>::isReady() const {
446 return core_->ready();
450 void Future<T>::raise(exception_wrapper exception) {
451 core_->raise(std::move(exception));
457 Future<typename std::decay<T>::type> makeFuture(T&& t) {
458 Promise<typename std::decay<T>::type> p;
459 p.setValue(std::forward<T>(t));
460 return p.getFuture();
463 inline // for multiple translation units
464 Future<void> makeFuture() {
467 return p.getFuture();
473 typename std::enable_if<!std::is_reference<F>::value, bool>::type sdf)
474 -> Future<decltype(func())> {
475 Promise<decltype(func())> p;
480 return p.getFuture();
484 auto makeFutureWith(F const& func) -> Future<decltype(func())> {
486 return makeFutureWith(std::move(copy));
490 Future<T> makeFuture(std::exception_ptr const& e) {
493 return p.getFuture();
497 Future<T> makeFuture(exception_wrapper ew) {
499 p.setException(std::move(ew));
500 return p.getFuture();
503 template <class T, class E>
504 typename std::enable_if<std::is_base_of<std::exception, E>::value,
506 makeFuture(E const& e) {
508 p.setException(make_exception_wrapper<E>(e));
509 return p.getFuture();
513 Future<T> makeFuture(Try<T>&& t) {
514 Promise<typename std::decay<T>::type> p;
515 p.setTry(std::move(t));
516 return p.getFuture();
520 inline Future<void> makeFuture(Try<void>&& t) {
521 if (t.hasException()) {
522 return makeFuture<void>(std::move(t.exception()));
529 Future<void> via(Executor* executor, int8_t priority) {
530 return makeFuture().via(executor, priority);
533 // mapSetCallback calls func(i, Try<T>) when every future completes
535 template <class T, class InputIterator, class F>
536 void mapSetCallback(InputIterator first, InputIterator last, F func) {
537 for (size_t i = 0; first != last; ++first, ++i) {
538 first->setCallback_([func, i](Try<T>&& t) {
539 func(i, std::move(t));
544 // collectAll (variadic)
546 template <typename... Fs>
547 typename detail::VariadicContext<
548 typename std::decay<Fs>::type::value_type...>::type
549 collectAll(Fs&&... fs) {
550 auto ctx = std::make_shared<detail::VariadicContext<
551 typename std::decay<Fs>::type::value_type...>>();
552 detail::collectAllVariadicHelper(ctx,
553 std::forward<typename std::decay<Fs>::type>(fs)...);
554 return ctx->p.getFuture();
557 // collectAll (iterator)
559 template <class InputIterator>
562 Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>>
563 collectAll(InputIterator first, InputIterator last) {
565 typename std::iterator_traits<InputIterator>::value_type::value_type T;
567 struct CollectAllContext {
568 CollectAllContext(int n) : results(n) {}
569 ~CollectAllContext() {
570 p.setValue(std::move(results));
572 Promise<std::vector<Try<T>>> p;
573 std::vector<Try<T>> results;
576 auto ctx = std::make_shared<CollectAllContext>(std::distance(first, last));
577 mapSetCallback<T>(first, last, [ctx](size_t i, Try<T>&& t) {
578 ctx->results[i] = std::move(t);
580 return ctx->p.getFuture();
585 template <typename T>
586 struct CollectContext {
587 struct Nothing { explicit Nothing(int n) {} };
589 using Result = typename std::conditional<
590 std::is_void<T>::value,
592 std::vector<T>>::type;
594 using InternalResult = typename std::conditional<
595 std::is_void<T>::value,
597 std::vector<Optional<T>>>::type;
599 explicit CollectContext(int n) : result(n) {}
601 if (!threw.exchange(true)) {
602 // map Optional<T> -> T
603 std::vector<T> finalResult;
604 finalResult.reserve(result.size());
605 std::transform(result.begin(), result.end(),
606 std::back_inserter(finalResult),
607 [](Optional<T>& o) { return std::move(o.value()); });
608 p.setValue(std::move(finalResult));
611 inline void setPartialResult(size_t i, Try<T>& t) {
612 result[i] = std::move(t.value());
615 InternalResult result;
616 std::atomic<bool> threw;
619 // Specialize for void (implementations in Future.cpp)
622 CollectContext<void>::~CollectContext();
625 void CollectContext<void>::setPartialResult(size_t i, Try<void>& t);
629 template <class InputIterator>
630 Future<typename detail::CollectContext<
631 typename std::iterator_traits<InputIterator>::value_type::value_type>::Result>
632 collect(InputIterator first, InputIterator last) {
634 typename std::iterator_traits<InputIterator>::value_type::value_type T;
636 auto ctx = std::make_shared<detail::CollectContext<T>>(
637 std::distance(first, last));
638 mapSetCallback<T>(first, last, [ctx](size_t i, Try<T>&& t) {
639 if (t.hasException()) {
640 if (!ctx->threw.exchange(true)) {
641 ctx->p.setException(std::move(t.exception()));
643 } else if (!ctx->threw) {
644 ctx->setPartialResult(i, t);
647 return ctx->p.getFuture();
650 template <class InputIterator>
655 std::iterator_traits<InputIterator>::value_type::value_type>>>
656 collectAny(InputIterator first, InputIterator last) {
658 typename std::iterator_traits<InputIterator>::value_type::value_type T;
660 struct CollectAnyContext {
661 CollectAnyContext(size_t n) : done(false) {};
662 Promise<std::pair<size_t, Try<T>>> p;
663 std::atomic<bool> done;
666 auto ctx = std::make_shared<CollectAnyContext>(std::distance(first, last));
667 mapSetCallback<T>(first, last, [ctx](size_t i, Try<T>&& t) {
668 if (!ctx->done.exchange(true)) {
669 ctx->p.setValue(std::make_pair(i, std::move(t)));
672 return ctx->p.getFuture();
675 template <class InputIterator>
676 Future<std::vector<std::pair<size_t, Try<typename
677 std::iterator_traits<InputIterator>::value_type::value_type>>>>
678 collectN(InputIterator first, InputIterator last, size_t n) {
680 std::iterator_traits<InputIterator>::value_type::value_type T;
681 typedef std::vector<std::pair<size_t, Try<T>>> V;
683 struct CollectNContext {
685 std::atomic<size_t> completed = {0};
688 auto ctx = std::make_shared<CollectNContext>();
690 if (std::distance(first, last) < n) {
691 ctx->p.setException(std::runtime_error("Not enough futures"));
693 // for each completed Future, increase count and add to vector, until we
694 // have n completed futures at which point we fulfil our Promise with the
696 mapSetCallback<T>(first, last, [ctx, n](size_t i, Try<T>&& t) {
697 auto c = ++ctx->completed;
699 assert(ctx->v.size() < n);
700 ctx->v.push_back(std::make_pair(i, std::move(t)));
702 ctx->p.setTry(Try<V>(std::move(ctx->v)));
708 return ctx->p.getFuture();
711 template <class It, class T, class F>
712 Future<T> reduce(It first, It last, T&& initial, F&& func) {
714 return makeFuture(std::move(initial));
717 typedef typename std::iterator_traits<It>::value_type::value_type ItT;
718 typedef typename std::conditional<
719 detail::callableWith<F, T&&, Try<ItT>&&>::value, Try<ItT>, ItT>::type Arg;
720 typedef isTry<Arg> IsTry;
722 folly::MoveWrapper<T> minitial(std::move(initial));
723 auto sfunc = std::make_shared<F>(std::move(func));
725 auto f = first->then([minitial, sfunc](Try<ItT>& head) mutable {
726 return (*sfunc)(std::move(*minitial),
727 head.template get<IsTry::value, Arg&&>());
730 for (++first; first != last; ++first) {
731 f = collectAll(f, *first).then([sfunc](std::tuple<Try<T>, Try<ItT>>& t) {
732 return (*sfunc)(std::move(std::get<0>(t).value()),
733 // Either return a ItT&& or a Try<ItT>&& depending
734 // on the type of the argument of func.
735 std::get<1>(t).template get<IsTry::value, Arg&&>());
743 template <class I, class F>
744 Future<I> Future<T>::reduce(I&& initial, F&& func) {
745 folly::MoveWrapper<I> minitial(std::move(initial));
746 folly::MoveWrapper<F> mfunc(std::move(func));
747 return then([minitial, mfunc](T& vals) mutable {
748 auto ret = std::move(*minitial);
749 for (auto& val : vals) {
750 ret = (*mfunc)(std::move(ret), std::move(val));
757 Future<T> Future<T>::within(Duration dur, Timekeeper* tk) {
758 return within(dur, TimedOut(), tk);
763 Future<T> Future<T>::within(Duration dur, E e, Timekeeper* tk) {
766 Context(E ex) : exception(std::move(ex)), promise(), token(false) {}
769 std::atomic<bool> token;
771 auto ctx = std::make_shared<Context>(std::move(e));
774 tk = folly::detail::getTimekeeperSingleton();
778 .then([ctx](Try<void> const& t) {
779 if (ctx->token.exchange(true) == false) {
780 if (t.hasException()) {
781 ctx->promise.setException(std::move(t.exception()));
783 ctx->promise.setException(std::move(ctx->exception));
788 this->then([ctx](Try<T>&& t) {
789 if (ctx->token.exchange(true) == false) {
790 ctx->promise.setTry(std::move(t));
794 return ctx->promise.getFuture();
798 Future<T> Future<T>::delayed(Duration dur, Timekeeper* tk) {
799 return collectAll(*this, futures::sleep(dur, tk))
800 .then([](std::tuple<Try<T>, Try<void>> tup) {
801 Try<T>& t = std::get<0>(tup);
802 return makeFuture<T>(std::move(t));
809 void waitImpl(Future<T>& f) {
810 // short-circuit if there's nothing to do
811 if (f.isReady()) return;
813 folly::fibers::Baton baton;
814 f = f.then([&](Try<T> t) {
816 return makeFuture(std::move(t));
820 // There's a race here between the return here and the actual finishing of
821 // the future. f is completed, but the setup may not have finished on done
822 // after the baton has posted.
823 while (!f.isReady()) {
824 std::this_thread::yield();
829 void waitImpl(Future<T>& f, Duration dur) {
830 // short-circuit if there's nothing to do
831 if (f.isReady()) return;
833 auto baton = std::make_shared<folly::fibers::Baton>();
834 f = f.then([baton](Try<T> t) {
836 return makeFuture(std::move(t));
839 // Let's preserve the invariant that if we did not timeout (timed_wait returns
840 // true), then the returned Future is complete when it is returned to the
841 // caller. We need to wait out the race for that Future to complete.
842 if (baton->timed_wait(dur)) {
843 while (!f.isReady()) {
844 std::this_thread::yield();
850 void waitViaImpl(Future<T>& f, DrivableExecutor* e) {
851 while (!f.isReady()) {
859 Future<T>& Future<T>::wait() & {
860 detail::waitImpl(*this);
865 Future<T>&& Future<T>::wait() && {
866 detail::waitImpl(*this);
867 return std::move(*this);
871 Future<T>& Future<T>::wait(Duration dur) & {
872 detail::waitImpl(*this, dur);
877 Future<T>&& Future<T>::wait(Duration dur) && {
878 detail::waitImpl(*this, dur);
879 return std::move(*this);
883 Future<T>& Future<T>::waitVia(DrivableExecutor* e) & {
884 detail::waitViaImpl(*this, e);
889 Future<T>&& Future<T>::waitVia(DrivableExecutor* e) && {
890 detail::waitViaImpl(*this, e);
891 return std::move(*this);
896 return std::move(wait().value());
900 inline void Future<void>::get() {
905 T Future<T>::get(Duration dur) {
908 return std::move(value());
915 inline void Future<void>::get(Duration dur) {
925 T Future<T>::getVia(DrivableExecutor* e) {
926 return std::move(waitVia(e).value());
930 inline void Future<void>::getVia(DrivableExecutor* e) {
937 static bool equals(const Try<T>& t1, const Try<T>& t2) {
938 return t1.value() == t2.value();
943 struct TryEquals<void> {
944 static bool equals(const Try<void>& t1, const Try<void>& t2) {
951 Future<bool> Future<T>::willEqual(Future<T>& f) {
952 return collectAll(*this, f).then([](const std::tuple<Try<T>, Try<T>>& t) {
953 if (std::get<0>(t).hasValue() && std::get<1>(t).hasValue()) {
954 return detail::TryEquals<T>::equals(std::get<0>(t), std::get<1>(t));
963 Future<T> Future<T>::filter(F predicate) {
964 auto p = folly::makeMoveWrapper(std::move(predicate));
965 return this->then([p](T val) {
966 T const& valConstRef = val;
967 if (!(*p)(valConstRef)) {
968 throw PredicateDoesNotObtain();
977 Future<Z> chainHelper(Future<Z> f) {
981 template <class Z, class F, class Fn, class... Callbacks>
982 Future<Z> chainHelper(F f, Fn fn, Callbacks... fns) {
983 return chainHelper<Z>(f.then(fn), fns...);
987 template <class A, class Z, class... Callbacks>
988 std::function<Future<Z>(Try<A>)>
989 chain(Callbacks... fns) {
990 MoveWrapper<Promise<A>> pw;
991 MoveWrapper<Future<Z>> fw(chainHelper<Z>(pw->getFuture(), fns...));
992 return [=](Try<A> t) mutable {
993 pw->setTry(std::move(t));
994 return std::move(*fw);
998 template <class It, class F, class ItT, class Result>
999 std::vector<Future<Result>> map(It first, It last, F func) {
1000 std::vector<Future<Result>> results;
1001 for (auto it = first; it != last; it++) {
1002 results.push_back(it->then(func));
1008 // Instantiate the most common Future types to save compile time
1009 extern template class Future<void>;
1010 extern template class Future<bool>;
1011 extern template class Future<int>;
1012 extern template class Future<int64_t>;
1013 extern template class Future<std::string>;
1014 extern template class Future<double>;
1016 } // namespace folly
1018 // I haven't included a Future<T&> specialization because I don't forsee us
1019 // using it, however it is not difficult to add when needed. Refer to
1020 // Future<void> for guidance. std::future and boost::future code would also be