//////////////////////////////////////////////////////////////////////////////// /// DISCLAIMER /// /// Copyright 2018 ArangoDB GmbH, Cologne, Germany /// /// Licensed under the Apache License, Version 2.0 (the "License"); /// you may not use this file except in compliance with the License. /// You may obtain a copy of the License at /// /// http://www.apache.org/licenses/LICENSE-2.0 /// /// Unless required by applicable law or agreed to in writing, software /// distributed under the License is distributed on an "AS IS" BASIS, /// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. /// See the License for the specific language governing permissions and /// limitations under the License. /// /// Copyright holder is ArangoDB GmbH, Cologne, Germany /// /// @author Simon Grätzer //////////////////////////////////////////////////////////////////////////////// #ifndef ARANGOD_FUTURES_UTILITIES_H #define ARANGOD_FUTURES_UTILITIES_H 1 #include #include #include #include #include #include "Futures/Future.h" #include "Futures/Unit.h" namespace arangodb { namespace futures { template Future makeFuture(Try&& t) { return Future(detail::SharedState::make(std::move(t))); } /// Make a complete void future Future makeFuture(); /// Make a completed Future by moving in a value. e.g. template Future::type> makeFuture(T&& t) { return makeFuture(Try::type>(std::forward(t))); } /// Make a failed Future from an std::exception_ptr. template Future makeFuture(std::exception_ptr const& e) { return makeFuture(Try(e)); } /// Make a Future from an exception type E that can be passed to /// std::make_exception_ptr(). template typename std::enable_if::value, Future>::type makeFuture(E const& e) { return makeFuture(Try(std::make_exception_ptr(e))); } // makeFutureWith(Future()) -> Future template > typename std::enable_if::value, R>::type makeFutureWith(F&& func) { using InnerType = typename isFuture::inner; try { return std::forward(func)(); } catch (...) { return makeFuture(std::current_exception()); } } // makeFutureWith(T()) -> Future // makeFutureWith(void()) -> Future template > typename std::enable_if::value, Future>::type makeFutureWith(F&& func) { return makeFuture( makeTryWith([&func]() mutable { return std::forward(func)(); })); } namespace detail { template void _foreach(F&&, size_t) {} template void _foreach(F&& f, size_t i, Arg&& arg, Args&&... args) { f(i, std::forward(arg)); _foreach(i + 1, std::forward(f), std::forward(args)...); } template void foreach (F&& f, Args && ... args) { _foreach(std::forward(f), 0, args...); } }; // namespace detail /// @brief When all the input Futures complete, the returned Future will complete. /// Errors do not cause early termination; this Future will always succeed /// after all its Futures have finished (whether successfully or with an /// error). /// The Futures are moved in, so your copies are invalid. If you need to /// chain further from these Futures, use the variant with an output iterator. /// This function is thread-safe for Futures running on different threads. /// It will complete in whichever thread the last Future completes in. /// @return for (Future, Future, ...) input is Future, Try, ...>>. //template //Future::inner>...>> collectAll(Fs&&... fs) { // using Result = std::tuple::inner>...>; // struct Context { // ~Context() { p.setValue(std::move(results)); } // Promise p; // Result results; // }; // auto ctx = std::make_shared(); // // detail::foreach ( // [&](auto i, auto&& f) { // f.then([i, ctx](auto&& t) { std::get(ctx->results) = std::move(t); }); // }, // std::move(fs)...); // return ctx->p.getFuture(); //} /// @brief When all the input Futures complete, the returned Future will /// complete. Errors do not cause early termination; this Future will always /// succeed after all its Futures have finished (whether successfully or with an /// error). /// The Futures are moved in, so your copies are invalid. If you need to /// chain further from these Futures, use the variant with an output iterator. /// This function is thread-safe for Futures running on different threads. But /// if you are doing anything non-trivial after, you will probably want to /// follow with `via(executor)` because it will complete in whichever thread the /// last Future completes in. /// The return type for Future input is a Future>> template Future::value_type::value_type>>> collectAll( InputIterator first, InputIterator last) { using FT = typename std::iterator_traits::value_type; using T = typename FT::value_type; struct Context { explicit Context(size_t n) : results(n) {} ~Context() { p.setValue(std::move(results)); } Promise>> p; std::vector> results; }; auto ctx = std::make_shared(size_t(std::distance(first, last))); for (size_t i = 0; first != last; ++first, ++i) { first->thenFinal([i, ctx](auto&& t) { ctx->results[i] = std::move(t); }); } return ctx->p.getFuture(); } template auto collectAll(Collection&& c) -> decltype(collectAll(std::begin(c), std::end(c))) { return collectAll(std::begin(c), std::end(c)); } } // namespace futures } // namespace arangodb #endif // ARANGOD_FUTURES_UTILITIES_H