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_);
48 typename std::enable_if<!isFuture<T2>::value, void*>::type>
49 Future<T>::Future(T2&& val) : core_(nullptr) {
51 p.setValue(std::forward<T2>(val));
52 *this = p.getFuture();
57 typename std::enable_if<
58 folly::is_void_or_unit<T2>::value,
60 Future<T>::Future() : core_(nullptr) {
63 *this = p.getFuture();
68 Future<T>::~Future() {
73 void Future<T>::detach() {
75 core_->detachFuture();
81 void Future<T>::throwIfInvalid() const {
88 void Future<T>::setCallback_(F&& func) {
90 core_->setCallback(std::move(func));
97 typename std::enable_if<isFuture<F>::value,
98 Future<typename isFuture<T>::Inner>>::type
100 return then([](Future<typename isFuture<T>::Inner> internal_future) {
101 return internal_future;
107 // Variant: returns a value
108 // e.g. f.then([](Try<T>&& t){ return t.value(); });
110 template <typename F, typename R, bool isTry, typename... Args>
111 typename std::enable_if<!R::ReturnsFuture::value, typename R::Return>::type
112 Future<T>::thenImplementation(F func, detail::argResult<isTry, F, Args...>) {
113 static_assert(sizeof...(Args) <= 1, "Then must take zero/one argument");
114 typedef typename R::ReturnsFuture::Inner B;
118 // wrap these so we can move them into the lambda
119 folly::MoveWrapper<Promise<B>> p;
120 folly::MoveWrapper<F> funcm(std::forward<F>(func));
122 // grab the Future now before we lose our handle on the Promise
123 auto f = p->getFuture();
125 f.setExecutor(getExecutor());
128 /* This is a bit tricky.
130 We can't just close over *this in case this Future gets moved. So we
131 make a new dummy Future. We could figure out something more
132 sophisticated that avoids making a new Future object when it can, as an
133 optimization. But this is correct.
135 core_ can't be moved, it is explicitly disallowed (as is copying). But
136 if there's ever a reason to allow it, this is one place that makes that
137 assumption and would need to be fixed. We use a standard shared pointer
138 for core_ (by copying it in), which means in essence obj holds a shared
139 pointer to itself. But this shouldn't leak because Promise will not
140 outlive the continuation, because Promise will setException() with a
141 broken Promise if it is destructed before completed. We could use a
142 weak pointer but it would have to be converted to a shared pointer when
143 func is executed (because the Future returned by func may possibly
144 persist beyond the callback, if it gets moved), and so it is an
145 optimization to just make it shared from the get-go.
147 We have to move in the Promise and func using the MoveWrapper
148 hack. (func could be copied but it's a big drag on perf).
150 Two subtle but important points about this design. detail::Core has no
151 back pointers to Future or Promise, so if Future or Promise get moved
152 (and they will be moved in performant code) we don't have to do
153 anything fancy. And because we store the continuation in the
154 detail::Core, not in the Future, we can execute the continuation even
155 after the Future has gone out of scope. This is an intentional design
156 decision. It is likely we will want to be able to cancel a continuation
157 in some circumstances, but I think it should be explicit not implicit
158 in the destruction of the Future used to create it.
161 [p, funcm](Try<T>&& t) mutable {
162 if (!isTry && t.hasException()) {
163 p->setException(std::move(t.exception()));
166 return (*funcm)(t.template get<isTry, Args>()...);
174 // Variant: returns a Future
175 // e.g. f.then([](T&& t){ return makeFuture<T>(t); });
177 template <typename F, typename R, bool isTry, typename... Args>
178 typename std::enable_if<R::ReturnsFuture::value, typename R::Return>::type
179 Future<T>::thenImplementation(F func, detail::argResult<isTry, F, Args...>) {
180 static_assert(sizeof...(Args) <= 1, "Then must take zero/one argument");
181 typedef typename R::ReturnsFuture::Inner B;
185 // wrap these so we can move them into the lambda
186 folly::MoveWrapper<Promise<B>> p;
187 folly::MoveWrapper<F> funcm(std::forward<F>(func));
189 // grab the Future now before we lose our handle on the Promise
190 auto f = p->getFuture();
192 f.setExecutor(getExecutor());
196 [p, funcm](Try<T>&& t) mutable {
197 if (!isTry && t.hasException()) {
198 p->setException(std::move(t.exception()));
201 auto f2 = (*funcm)(t.template get<isTry, Args>()...);
202 // that didn't throw, now we can steal p
203 f2.setCallback_([p](Try<B>&& b) mutable {
204 p->setTry(std::move(b));
206 } catch (const std::exception& e) {
207 p->setException(exception_wrapper(std::current_exception(), e));
209 p->setException(exception_wrapper(std::current_exception()));
217 template <typename T>
218 template <typename R, typename Caller, typename... Args>
219 Future<typename isFuture<R>::Inner>
220 Future<T>::then(R(Caller::*func)(Args...), Caller *instance) {
221 typedef typename std::remove_cv<
222 typename std::remove_reference<
223 typename detail::ArgType<Args...>::FirstArg>::type>::type FirstArg;
224 return then([instance, func](Try<T>&& t){
225 return (instance->*func)(t.template get<isTry<FirstArg>::value, Args>()...);
230 template <class Executor, class Arg, class... Args>
231 auto Future<T>::then(Executor* x, Arg&& arg, Args&&... args)
232 -> decltype(this->then(std::forward<Arg>(arg),
233 std::forward<Args>(args)...))
235 auto oldX = getExecutor();
237 return this->then(std::forward<Arg>(arg), std::forward<Args>(args)...).
242 Future<void> Future<T>::then() {
243 return then([] (Try<T>&& t) {});
246 // onError where the callback returns T
249 typename std::enable_if<
250 !detail::callableWith<F, exception_wrapper>::value &&
251 !detail::Extract<F>::ReturnsFuture::value,
253 Future<T>::onError(F&& func) {
254 typedef typename detail::Extract<F>::FirstArg Exn;
256 std::is_same<typename detail::Extract<F>::RawReturn, T>::value,
257 "Return type of onError callback must be T or Future<T>");
260 auto f = p.getFuture();
261 auto pm = folly::makeMoveWrapper(std::move(p));
262 auto funcm = folly::makeMoveWrapper(std::move(func));
263 setCallback_([pm, funcm](Try<T>&& t) mutable {
264 if (!t.template withException<Exn>([&] (Exn& e) {
269 pm->setTry(std::move(t));
276 // onError where the callback returns Future<T>
279 typename std::enable_if<
280 !detail::callableWith<F, exception_wrapper>::value &&
281 detail::Extract<F>::ReturnsFuture::value,
283 Future<T>::onError(F&& func) {
285 std::is_same<typename detail::Extract<F>::Return, Future<T>>::value,
286 "Return type of onError callback must be T or Future<T>");
287 typedef typename detail::Extract<F>::FirstArg Exn;
290 auto f = p.getFuture();
291 auto pm = folly::makeMoveWrapper(std::move(p));
292 auto funcm = folly::makeMoveWrapper(std::move(func));
293 setCallback_([pm, funcm](Try<T>&& t) mutable {
294 if (!t.template withException<Exn>([&] (Exn& e) {
296 auto f2 = (*funcm)(e);
297 f2.setCallback_([pm](Try<T>&& t2) mutable {
298 pm->setTry(std::move(t2));
300 } catch (const std::exception& e2) {
301 pm->setException(exception_wrapper(std::current_exception(), e2));
303 pm->setException(exception_wrapper(std::current_exception()));
306 pm->setTry(std::move(t));
315 Future<T> Future<T>::ensure(F func) {
316 MoveWrapper<F> funcw(std::move(func));
317 return this->then([funcw](Try<T>&& t) {
319 return makeFuture(std::move(t));
325 Future<T> Future<T>::onTimeout(Duration dur, F&& func, Timekeeper* tk) {
326 auto funcw = folly::makeMoveWrapper(std::forward<F>(func));
327 return within(dur, tk)
328 .onError([funcw](TimedOut const&) { return (*funcw)(); });
333 typename std::enable_if<
334 detail::callableWith<F, exception_wrapper>::value &&
335 detail::Extract<F>::ReturnsFuture::value,
337 Future<T>::onError(F&& func) {
339 std::is_same<typename detail::Extract<F>::Return, Future<T>>::value,
340 "Return type of onError callback must be T or Future<T>");
343 auto f = p.getFuture();
344 auto pm = folly::makeMoveWrapper(std::move(p));
345 auto funcm = folly::makeMoveWrapper(std::move(func));
346 setCallback_([pm, funcm](Try<T> t) mutable {
347 if (t.hasException()) {
349 auto f2 = (*funcm)(std::move(t.exception()));
350 f2.setCallback_([pm](Try<T> t2) mutable {
351 pm->setTry(std::move(t2));
353 } catch (const std::exception& e2) {
354 pm->setException(exception_wrapper(std::current_exception(), e2));
356 pm->setException(exception_wrapper(std::current_exception()));
359 pm->setTry(std::move(t));
366 // onError(exception_wrapper) that returns T
369 typename std::enable_if<
370 detail::callableWith<F, exception_wrapper>::value &&
371 !detail::Extract<F>::ReturnsFuture::value,
373 Future<T>::onError(F&& func) {
375 std::is_same<typename detail::Extract<F>::Return, Future<T>>::value,
376 "Return type of onError callback must be T or Future<T>");
379 auto f = p.getFuture();
380 auto pm = folly::makeMoveWrapper(std::move(p));
381 auto funcm = folly::makeMoveWrapper(std::move(func));
382 setCallback_([pm, funcm](Try<T> t) mutable {
383 if (t.hasException()) {
385 return (*funcm)(std::move(t.exception()));
388 pm->setTry(std::move(t));
396 typename std::add_lvalue_reference<T>::type Future<T>::value() {
399 return core_->getTry().value();
403 typename std::add_lvalue_reference<const T>::type Future<T>::value() const {
406 return core_->getTry().value();
410 Try<T>& Future<T>::getTry() {
413 return core_->getTry();
417 Optional<Try<T>> Future<T>::poll() {
419 if (core_->ready()) {
420 o = std::move(core_->getTry());
426 inline Future<T> Future<T>::via(Executor* executor) && {
429 setExecutor(executor);
431 return std::move(*this);
435 inline Future<T> Future<T>::via(Executor* executor) & {
438 MoveWrapper<Promise<T>> p;
439 auto f = p->getFuture();
440 then([p](Try<T>&& t) mutable { p->setTry(std::move(t)); });
441 return std::move(f).via(executor);
445 bool Future<T>::isReady() const {
447 return core_->ready();
451 void Future<T>::raise(exception_wrapper exception) {
452 core_->raise(std::move(exception));
458 Future<typename std::decay<T>::type> makeFuture(T&& t) {
459 Promise<typename std::decay<T>::type> p;
460 p.setValue(std::forward<T>(t));
461 return p.getFuture();
464 inline // for multiple translation units
465 Future<void> makeFuture() {
468 return p.getFuture();
474 typename std::enable_if<!std::is_reference<F>::value, bool>::type sdf)
475 -> Future<decltype(func())> {
476 Promise<decltype(func())> p;
481 return p.getFuture();
485 auto makeFutureWith(F const& func) -> Future<decltype(func())> {
487 return makeFutureWith(std::move(copy));
491 Future<T> makeFuture(std::exception_ptr const& e) {
494 return p.getFuture();
498 Future<T> makeFuture(exception_wrapper ew) {
500 p.setException(std::move(ew));
501 return p.getFuture();
504 template <class T, class E>
505 typename std::enable_if<std::is_base_of<std::exception, E>::value,
507 makeFuture(E const& e) {
509 p.setException(make_exception_wrapper<E>(e));
510 return p.getFuture();
514 Future<T> makeFuture(Try<T>&& t) {
515 Promise<typename std::decay<T>::type> p;
516 p.setTry(std::move(t));
517 return p.getFuture();
521 inline Future<void> makeFuture(Try<void>&& t) {
522 if (t.hasException()) {
523 return makeFuture<void>(std::move(t.exception()));
530 inline Future<void> via(Executor* executor) {
531 return makeFuture().via(executor);
534 // mapSetCallback calls func(i, Try<T>) when every future completes
536 template <class T, class InputIterator, class F>
537 void mapSetCallback(InputIterator first, InputIterator last, F func) {
538 for (size_t i = 0; first != last; ++first, ++i) {
539 first->setCallback_([func, i](Try<T>&& t) {
540 func(i, std::move(t));
545 // collectAll (variadic)
547 template <typename... Fs>
548 typename detail::VariadicContext<
549 typename std::decay<Fs>::type::value_type...>::type
550 collectAll(Fs&&... fs) {
551 auto ctx = std::make_shared<detail::VariadicContext<
552 typename std::decay<Fs>::type::value_type...>>();
553 detail::collectAllVariadicHelper(ctx,
554 std::forward<typename std::decay<Fs>::type>(fs)...);
555 return ctx->p.getFuture();
558 // collectAll (iterator)
560 template <class InputIterator>
563 Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>>
564 collectAll(InputIterator first, InputIterator last) {
566 typename std::iterator_traits<InputIterator>::value_type::value_type T;
568 struct CollectAllContext {
569 CollectAllContext(int n) : results(n) {}
570 ~CollectAllContext() {
571 p.setValue(std::move(results));
573 Promise<std::vector<Try<T>>> p;
574 std::vector<Try<T>> results;
577 auto ctx = std::make_shared<CollectAllContext>(std::distance(first, last));
578 mapSetCallback<T>(first, last, [ctx](size_t i, Try<T>&& t) {
579 ctx->results[i] = std::move(t);
581 return ctx->p.getFuture();
586 template <typename T>
587 struct CollectContext {
588 struct Nothing { explicit Nothing(int n) {} };
590 using Result = typename std::conditional<
591 std::is_void<T>::value,
593 std::vector<T>>::type;
595 using InternalResult = typename std::conditional<
596 std::is_void<T>::value,
598 std::vector<Optional<T>>>::type;
600 explicit CollectContext(int n) : result(n) {}
602 if (!threw.exchange(true)) {
603 // map Optional<T> -> T
604 std::vector<T> finalResult;
605 finalResult.reserve(result.size());
606 std::transform(result.begin(), result.end(),
607 std::back_inserter(finalResult),
608 [](Optional<T>& o) { return std::move(o.value()); });
609 p.setValue(std::move(finalResult));
612 inline void setPartialResult(size_t i, Try<T>& t) {
613 result[i] = std::move(t.value());
616 InternalResult result;
617 std::atomic<bool> threw;
620 // Specialize for void (implementations in Future.cpp)
623 CollectContext<void>::~CollectContext();
626 void CollectContext<void>::setPartialResult(size_t i, Try<void>& t);
630 template <class InputIterator>
631 Future<typename detail::CollectContext<
632 typename std::iterator_traits<InputIterator>::value_type::value_type>::Result>
633 collect(InputIterator first, InputIterator last) {
635 typename std::iterator_traits<InputIterator>::value_type::value_type T;
637 auto ctx = std::make_shared<detail::CollectContext<T>>(
638 std::distance(first, last));
639 mapSetCallback<T>(first, last, [ctx](size_t i, Try<T>&& t) {
640 if (t.hasException()) {
641 if (!ctx->threw.exchange(true)) {
642 ctx->p.setException(std::move(t.exception()));
644 } else if (!ctx->threw) {
645 ctx->setPartialResult(i, t);
648 return ctx->p.getFuture();
651 template <class InputIterator>
656 std::iterator_traits<InputIterator>::value_type::value_type>>>
657 collectAny(InputIterator first, InputIterator last) {
659 typename std::iterator_traits<InputIterator>::value_type::value_type T;
661 struct CollectAnyContext {
662 CollectAnyContext(size_t n) : done(false) {};
663 Promise<std::pair<size_t, Try<T>>> p;
664 std::atomic<bool> done;
667 auto ctx = std::make_shared<CollectAnyContext>(std::distance(first, last));
668 mapSetCallback<T>(first, last, [ctx](size_t i, Try<T>&& t) {
669 if (!ctx->done.exchange(true)) {
670 ctx->p.setValue(std::make_pair(i, std::move(t)));
673 return ctx->p.getFuture();
676 template <class InputIterator>
677 Future<std::vector<std::pair<size_t, Try<typename
678 std::iterator_traits<InputIterator>::value_type::value_type>>>>
679 collectN(InputIterator first, InputIterator last, size_t n) {
681 std::iterator_traits<InputIterator>::value_type::value_type T;
682 typedef std::vector<std::pair<size_t, Try<T>>> V;
684 struct CollectNContext {
686 std::atomic<size_t> completed = {0};
689 auto ctx = std::make_shared<CollectNContext>();
691 if (std::distance(first, last) < n) {
692 ctx->p.setException(std::runtime_error("Not enough futures"));
694 // for each completed Future, increase count and add to vector, until we
695 // have n completed futures at which point we fulfil our Promise with the
697 mapSetCallback<T>(first, last, [ctx, n](size_t i, Try<T>&& t) {
698 auto c = ++ctx->completed;
700 assert(ctx->v.size() < n);
701 ctx->v.push_back(std::make_pair(i, std::move(t)));
703 ctx->p.setTry(Try<V>(std::move(ctx->v)));
709 return ctx->p.getFuture();
712 template <class It, class T, class F>
713 Future<T> reduce(It first, It last, T&& initial, F&& func) {
715 return makeFuture(std::move(initial));
718 typedef typename std::iterator_traits<It>::value_type::value_type ItT;
719 typedef typename std::conditional<
720 detail::callableWith<F, T&&, Try<ItT>&&>::value, Try<ItT>, ItT>::type Arg;
721 typedef isTry<Arg> IsTry;
723 folly::MoveWrapper<T> minitial(std::move(initial));
724 auto sfunc = std::make_shared<F>(std::move(func));
726 auto f = first->then([minitial, sfunc](Try<ItT>& head) mutable {
727 return (*sfunc)(std::move(*minitial),
728 head.template get<IsTry::value, Arg&&>());
731 for (++first; first != last; ++first) {
732 f = collectAll(f, *first).then([sfunc](std::tuple<Try<T>, Try<ItT>>& t) {
733 return (*sfunc)(std::move(std::get<0>(t).value()),
734 // Either return a ItT&& or a Try<ItT>&& depending
735 // on the type of the argument of func.
736 std::get<1>(t).template get<IsTry::value, Arg&&>());
744 template <class I, class F>
745 Future<I> Future<T>::reduce(I&& initial, F&& func) {
746 folly::MoveWrapper<I> minitial(std::move(initial));
747 folly::MoveWrapper<F> mfunc(std::move(func));
748 return then([minitial, mfunc](T& vals) mutable {
749 auto ret = std::move(*minitial);
750 for (auto& val : vals) {
751 ret = (*mfunc)(std::move(ret), std::move(val));
758 Future<T> Future<T>::within(Duration dur, Timekeeper* tk) {
759 return within(dur, TimedOut(), tk);
764 Future<T> Future<T>::within(Duration dur, E e, Timekeeper* tk) {
767 Context(E ex) : exception(std::move(ex)), promise(), token(false) {}
770 std::atomic<bool> token;
772 auto ctx = std::make_shared<Context>(std::move(e));
775 tk = folly::detail::getTimekeeperSingleton();
779 .then([ctx](Try<void> const& t) {
780 if (ctx->token.exchange(true) == false) {
781 if (t.hasException()) {
782 ctx->promise.setException(std::move(t.exception()));
784 ctx->promise.setException(std::move(ctx->exception));
789 this->then([ctx](Try<T>&& t) {
790 if (ctx->token.exchange(true) == false) {
791 ctx->promise.setTry(std::move(t));
795 return ctx->promise.getFuture();
799 Future<T> Future<T>::delayed(Duration dur, Timekeeper* tk) {
800 return collectAll(*this, futures::sleep(dur, tk))
801 .then([](std::tuple<Try<T>, Try<void>> tup) {
802 Try<T>& t = std::get<0>(tup);
803 return makeFuture<T>(std::move(t));
810 void waitImpl(Future<T>& f) {
811 // short-circuit if there's nothing to do
812 if (f.isReady()) return;
814 folly::fibers::Baton baton;
815 f = f.then([&](Try<T> t) {
817 return makeFuture(std::move(t));
821 // There's a race here between the return here and the actual finishing of
822 // the future. f is completed, but the setup may not have finished on done
823 // after the baton has posted.
824 while (!f.isReady()) {
825 std::this_thread::yield();
830 void waitImpl(Future<T>& f, Duration dur) {
831 // short-circuit if there's nothing to do
832 if (f.isReady()) return;
834 auto baton = std::make_shared<folly::fibers::Baton>();
835 f = f.then([baton](Try<T> t) {
837 return makeFuture(std::move(t));
840 // Let's preserve the invariant that if we did not timeout (timed_wait returns
841 // true), then the returned Future is complete when it is returned to the
842 // caller. We need to wait out the race for that Future to complete.
843 if (baton->timed_wait(dur)) {
844 while (!f.isReady()) {
845 std::this_thread::yield();
851 void waitViaImpl(Future<T>& f, DrivableExecutor* e) {
852 while (!f.isReady()) {
860 Future<T>& Future<T>::wait() & {
861 detail::waitImpl(*this);
866 Future<T>&& Future<T>::wait() && {
867 detail::waitImpl(*this);
868 return std::move(*this);
872 Future<T>& Future<T>::wait(Duration dur) & {
873 detail::waitImpl(*this, dur);
878 Future<T>&& Future<T>::wait(Duration dur) && {
879 detail::waitImpl(*this, dur);
880 return std::move(*this);
884 Future<T>& Future<T>::waitVia(DrivableExecutor* e) & {
885 detail::waitViaImpl(*this, e);
890 Future<T>&& Future<T>::waitVia(DrivableExecutor* e) && {
891 detail::waitViaImpl(*this, e);
892 return std::move(*this);
897 return std::move(wait().value());
901 inline void Future<void>::get() {
906 T Future<T>::get(Duration dur) {
909 return std::move(value());
916 inline void Future<void>::get(Duration dur) {
926 T Future<T>::getVia(DrivableExecutor* e) {
927 return std::move(waitVia(e).value());
931 inline void Future<void>::getVia(DrivableExecutor* e) {
938 static bool equals(const Try<T>& t1, const Try<T>& t2) {
939 return t1.value() == t2.value();
944 struct TryEquals<void> {
945 static bool equals(const Try<void>& t1, const Try<void>& t2) {
952 Future<bool> Future<T>::willEqual(Future<T>& f) {
953 return collectAll(*this, f).then([](const std::tuple<Try<T>, Try<T>>& t) {
954 if (std::get<0>(t).hasValue() && std::get<1>(t).hasValue()) {
955 return detail::TryEquals<T>::equals(std::get<0>(t), std::get<1>(t));
964 Future<T> Future<T>::filter(F predicate) {
965 auto p = folly::makeMoveWrapper(std::move(predicate));
966 return this->then([p](T val) {
967 T const& valConstRef = val;
968 if (!(*p)(valConstRef)) {
969 throw PredicateDoesNotObtain();
978 Future<Z> chainHelper(Future<Z> f) {
982 template <class Z, class F, class Fn, class... Callbacks>
983 Future<Z> chainHelper(F f, Fn fn, Callbacks... fns) {
984 return chainHelper<Z>(f.then(fn), fns...);
988 template <class A, class Z, class... Callbacks>
989 std::function<Future<Z>(Try<A>)>
990 chain(Callbacks... fns) {
991 MoveWrapper<Promise<A>> pw;
992 MoveWrapper<Future<Z>> fw(chainHelper<Z>(pw->getFuture(), fns...));
993 return [=](Try<A> t) mutable {
994 pw->setTry(std::move(t));
995 return std::move(*fw);
999 template <class It, class F, class ItT, class Result>
1000 std::vector<Future<Result>> map(It first, It last, F func) {
1001 std::vector<Future<Result>> results;
1002 for (auto it = first; it != last; it++) {
1003 results.push_back(it->then(func));
1009 // Instantiate the most common Future types to save compile time
1010 extern template class Future<void>;
1011 extern template class Future<bool>;
1012 extern template class Future<int>;
1013 extern template class Future<int64_t>;
1014 extern template class Future<std::string>;
1015 extern template class Future<double>;
1017 } // namespace folly
1019 // I haven't included a Future<T&> specialization because I don't forsee us
1020 // using it, however it is not difficult to add when needed. Refer to
1021 // Future<void> for guidance. std::future and boost::future code would also be