/* * Copyright (C) 2014 Cloudius Systems, Ltd. */ #ifndef CORE_FUTURE_UTIL_HH_ #define CORE_FUTURE_UTIL_HH_ #include "future.hh" #include "shared_ptr.hh" #include "reactor.hh" #include // parallel_for_each - run tasks in parallel // // Given a range [@begin, @end) of objects, run func(*i) for each i in // the range, and return a future<> that resolves when all the functions // complete. @func should return a future<> that indicates when it is // complete. template inline future<> parallel_for_each(Iterator begin, Iterator end, Func&& func) { auto ret = make_ready_future<>(); while (begin != end) { auto f = func(*begin++).then([ret = std::move(ret)] () mutable { return std::move(ret); }); ret = std::move(f); } return ret; } // The AsyncAction concept represents an action which can complete later than // the actual function invocation. It is represented by a function which // returns a future which resolves when the action is done. template static inline void do_until_continued(StopCondition&& stop_cond, AsyncAction&& action, promise<> p) { while (!stop_cond()) { auto&& f = action(); if (!f.available()) { f.then([action = std::forward(action), stop_cond = std::forward(stop_cond), p = std::move(p)]() mutable { do_until_continued(stop_cond, std::forward(action), std::move(p)); }); return; } if (f.failed()) { f.forward_to(std::move(p)); return; } } p.set_value(); } // Invokes given action until it fails or given condition evaluates to true. template static inline future<> do_until(StopCondition&& stop_cond, AsyncAction&& action) { promise<> p; auto f = p.get_future(); do_until_continued(std::forward(stop_cond), std::forward(action), std::move(p)); return f; } // Invoke given action until it fails. template static inline future<> keep_doing(AsyncAction&& action) { while (task_quota) { auto f = action(); if (!f.available()) { return f.then([action = std::forward(action)] () mutable { return keep_doing(std::forward(action)); }); } if (f.failed()) { return std::move(f); } --task_quota; } promise<> p; auto f = p.get_future(); schedule(make_task([action = std::forward(action), p = std::move(p)] () mutable { keep_doing(std::forward(action)).forward_to(std::move(p)); })); return f; } template static inline future<> do_for_each(Iterator begin, Iterator end, AsyncAction&& action) { if (begin == end) { return make_ready_future<>(); } while (true) { auto f = action(*begin++); if (begin == end) { return f; } if (!f.available()) { return std::move(f).then([action = std::forward(action), begin = std::move(begin), end = std::move(end)] () mutable { return do_for_each(std::move(begin), std::move(end), std::forward(action)); }); } if (f.failed()) { return std::move(f); } } } template future> when_all(Future&&... fut); template <> inline future> when_all() { return make_ready_future>(); } // gcc can't capture a parameter pack, so we need to capture // a tuple and use apply. But apply cannot accept an overloaded // function pointer as its first parameter, so provide this instead. struct do_when_all { template future> operator()(Future&&... fut) const { return when_all(std::move(fut)...); } }; template inline future> when_all(Future&& fut, Rest&&... rest) { return std::move(fut).then_wrapped( [rest = std::make_tuple(std::move(rest)...)] (Future&& fut) mutable { return apply(do_when_all(), std::move(rest)).then_wrapped( [fut = std::move(fut)] (future>&& rest) mutable { return make_ready_future>( std::tuple_cat(std::make_tuple(std::move(fut)), std::get<0>(rest.get()))); }); }); } template struct reducer_with_get_traits { using result_type = decltype(std::declval().get()); using future_type = future; static future_type maybe_call_get(future<> f, lw_shared_ptr r) { return f.then([r = std::move(r)] () mutable { return make_ready_future(std::move(*r).get()); }); } }; template struct reducer_traits { using future_type = future<>; static future_type maybe_call_get(future<> f, lw_shared_ptr r) { return f.then([r = std::move(r)] {}); } }; template struct reducer_traits().get(), void())> : public reducer_with_get_traits {}; // @Mapper is a callable which transforms values from the iterator range // into a future. @Reducer is an object which can be called with T as // parameter and yields a future<>. It may have a get() method which returns // a value of type U which holds the result of reduction. This value is wrapped // in a future and returned by this function. If the reducer has no get() method // then this function returns future<>. // // TODO: specialize for non-deferring reducer template inline auto map_reduce(Iterator begin, Iterator end, Mapper&& mapper, Reducer&& r) -> typename reducer_traits::future_type { auto r_ptr = make_lw_shared(std::forward(r)); future<> ret = make_ready_future<>(); while (begin != end) { ret = mapper(*begin++).then([ret = std::move(ret), r_ptr] (auto value) mutable { return ret.then([value = std::move(value), r_ptr] () mutable { return (*r_ptr)(std::move(value)); }); }); } return reducer_traits::maybe_call_get(std::move(ret), r_ptr); } // Implements @Reducer concept. Calculates the result by // adding elements to the accumulator. template class adder { private: Result _result; public: future<> operator()(const Addend& value) { _result += value; return make_ready_future<>(); } Result get() && { return std::move(_result); } }; static inline future<> now() { return make_ready_future<>(); } #endif /* CORE_FUTURE_UTIL_HH_ */