1 // Copyright (c) 2007-2017 Hartmut Kaiser 2 // Copyright (c) 2013 Agustin Berge 3 // Copyright (c) 2017 Denis Blank 4 // 5 // Distributed under the Boost Software License, Version 1.0. (See accompanying 6 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 7 8 /// \file lcos/when_all.hpp 9 10 #if !defined(HPX_LCOS_WHEN_ALL_APR_19_2012_1140AM) 11 #define HPX_LCOS_WHEN_ALL_APR_19_2012_1140AM 12 13 #if defined(DOXYGEN) 14 namespace hpx 15 { 16 /// The function \a when_all is an operator allowing to join on the result 17 /// of all given futures. It AND-composes all future objects given and 18 /// returns a new future object representing the same list of futures 19 /// after they finished executing. 20 /// 21 /// \param first [in] The iterator pointing to the first element of a 22 /// sequence of \a future or \a shared_future objects for 23 /// which \a when_all should wait. 24 /// \param last [in] The iterator pointing to the last element of a 25 /// sequence of \a future or \a shared_future objects for 26 /// which \a when_all should wait. 27 /// 28 /// \return Returns a future holding the same list of futures as has 29 /// been passed to \a when_all. 30 /// - future<Container<future<R>>>: If the input cardinality is 31 /// unknown at compile time and the futures are all of the 32 /// same type. The order of the futures in the output container 33 /// will be the same as given by the input iterator. 34 /// 35 /// \note Calling this version of \a when_all where first == last, returns 36 /// a future with an empty container that is immediately ready. 37 /// Each future and shared_future is waited upon and then copied into 38 /// the collection of the output (returned) future, maintaining the 39 /// order of the futures in the input collection. 40 /// The future returned by \a when_all will not throw an exception, 41 /// but the futures held in the output collection may. 42 template <typename InputIter, typename Container = 43 vector<future<typename std::iterator_traits<InputIter>::value_type>>> 44 future<Container> 45 when_all(InputIter first, InputIter last); 46 47 /// The function \a when_all is an operator allowing to join on the result 48 /// of all given futures. It AND-composes all future objects given and 49 /// returns a new future object representing the same list of futures 50 /// after they finished executing. 51 /// 52 /// \param values [in] A range holding an arbitrary amount of \a future 53 /// or \a shared_future objects for which \a when_all 54 /// should wait. 55 /// 56 /// \return Returns a future holding the same list of futures as has 57 /// been passed to when_all. 58 /// - future<Container<future<R>>>: If the input cardinality is 59 /// unknown at compile time and the futures are all of the 60 /// same type. 61 /// 62 /// \note Calling this version of \a when_all where the input container is 63 /// empty, returns a future with an empty container that is immediately 64 /// ready. 65 /// Each future and shared_future is waited upon and then copied into 66 /// the collection of the output (returned) future, maintaining the 67 /// order of the futures in the input collection. 68 /// The future returned by \a when_all will not throw an exception, 69 /// but the futures held in the output collection may. 70 template <typename Range> 71 future<Range> 72 when_all(Range&& values); 73 74 /// The function \a when_all is an operator allowing to join on the result 75 /// of all given futures. It AND-composes all future objects given and 76 /// returns a new future object representing the same list of futures 77 /// after they finished executing. 78 /// 79 /// \param futures [in] An arbitrary number of \a future or \a shared_future 80 /// objects, possibly holding different types for which 81 /// \a when_all should wait. 82 /// 83 /// \return Returns a future holding the same list of futures as has 84 /// been passed to \a when_all. 85 /// - future<tuple<future<T0>, future<T1>, future<T2>...>>: If 86 /// inputs are fixed in number and are of heterogeneous types. 87 /// The inputs can be any arbitrary number of future objects. 88 /// - future<tuple<>> if \a when_all is called with zero arguments. 89 /// The returned future will be initially ready. 90 /// 91 /// \note Each future and shared_future is waited upon and then copied into 92 /// the collection of the output (returned) future, maintaining the 93 /// order of the futures in the input collection. 94 /// The future returned by \a when_all will not throw an exception, 95 /// but the futures held in the output collection may. 96 template <typename ...T> 97 future<tuple<future<T>...>> 98 when_all(T &&... futures); 99 100 /// The function \a when_all_n is an operator allowing to join on the result 101 /// of all given futures. It AND-composes all future objects given and 102 /// returns a new future object representing the same list of futures 103 /// after they finished executing. 104 /// 105 /// \param begin [in] The iterator pointing to the first element of a 106 /// sequence of \a future or \a shared_future objects for 107 /// which \a wait_all_n should wait. 108 /// \param count [in] The number of elements in the sequence starting at 109 /// \a first. 110 /// 111 /// \return Returns a future holding the same list of futures as has 112 /// been passed to \a when_all_n. 113 /// - future<Container<future<R>>>: If the input cardinality is 114 /// unknown at compile time and the futures are all of the 115 /// same type. The order of the futures in the output vector 116 /// will be the same as given by the input iterator. 117 /// 118 /// \throws This function will throw errors which are encountered while 119 /// setting up the requested operation only. Errors encountered 120 /// while executing the operations delivering the results to be 121 /// stored in the futures are reported through the futures 122 /// themselves. 123 /// 124 /// \note As long as \a ec is not pre-initialized to \a hpx::throws this 125 /// function doesn't throw but returns the result code using the 126 /// parameter \a ec. Otherwise it throws an instance of 127 /// hpx::exception. 128 /// 129 /// \note None of the futures in the input sequence are invalidated. 130 template <typename InputIter, typename Container = 131 vector<future<typename std::iterator_traits<InputIter>::value_type>>> 132 future<Container> 133 when_all_n(InputIter begin, std::size_t count); 134 } 135 136 #else // DOXYGEN 137 138 #include <hpx/config.hpp> 139 #include <hpx/lcos/detail/future_data.hpp> 140 #include <hpx/lcos/detail/future_traits.hpp> 141 #include <hpx/lcos/detail/future_transforms.hpp> 142 #include <hpx/lcos/future.hpp> 143 #include <hpx/traits/acquire_future.hpp> 144 #include <hpx/traits/future_access.hpp> 145 #include <hpx/traits/is_future.hpp> 146 #include <hpx/traits/is_future_range.hpp> 147 #include <hpx/util/internal_allocator.hpp> 148 #include <hpx/util/pack_traversal_async.hpp> 149 #include <hpx/util/tuple.hpp> 150 151 #include <cstddef> 152 #include <iterator> 153 #include <type_traits> 154 #include <utility> 155 #include <vector> 156 157 /////////////////////////////////////////////////////////////////////////////// 158 namespace hpx { namespace lcos 159 { 160 namespace detail 161 { 162 /////////////////////////////////////////////////////////////////////// 163 template <typename T, typename Enable = void> 164 struct when_all_result 165 { 166 typedef T type; 167 callhpx::lcos::detail::when_all_result168 static type call(T&& t) 169 { 170 return std::move(t); 171 } 172 }; 173 174 template <typename T> 175 struct when_all_result<util::tuple<T>, 176 typename std::enable_if< 177 traits::is_future_range<T>::value 178 >::type> 179 { 180 typedef T type; 181 callhpx::lcos::detail::when_all_result182 static type call(util::tuple<T>&& t) 183 { 184 return std::move(util::get<0>(t)); 185 } 186 }; 187 188 template <typename Tuple> 189 class async_when_all_frame 190 : public future_data<typename when_all_result<Tuple>::type> 191 { 192 public: 193 typedef typename when_all_result<Tuple>::type result_type; 194 typedef hpx::lcos::future<result_type> type; 195 typedef hpx::lcos::detail::future_data<result_type> base_type; 196 async_when_all_frame(typename base_type::init_no_addref no_addref)197 explicit async_when_all_frame( 198 typename base_type::init_no_addref no_addref) 199 : future_data<typename when_all_result<Tuple>::type>(no_addref) 200 { 201 } 202 203 template <typename T> operator ()(util::async_traverse_visit_tag,T && current)204 auto operator()(util::async_traverse_visit_tag, T&& current) 205 -> decltype(async_visit_future(std::forward<T>(current))) 206 { 207 return async_visit_future(std::forward<T>(current)); 208 } 209 210 template <typename T, typename N> operator ()(util::async_traverse_detach_tag,T && current,N && next)211 auto operator()( 212 util::async_traverse_detach_tag, T&& current, N&& next) 213 -> decltype(async_detach_future( 214 std::forward<T>(current), std::forward<N>(next))) 215 { 216 return async_detach_future( 217 std::forward<T>(current), std::forward<N>(next)); 218 } 219 220 template <typename T> operator ()(util::async_traverse_complete_tag,T && pack)221 void operator()(util::async_traverse_complete_tag, T&& pack) 222 { 223 this->set_value( 224 when_all_result<Tuple>::call(std::forward<T>(pack))); 225 } 226 }; 227 228 template <typename... T> 229 typename detail::async_when_all_frame< 230 util::tuple< 231 typename traits::acquire_future<T>::type... 232 > 233 >::type when_all_impl(T &&...args)234 when_all_impl(T&&... args) 235 { 236 typedef util::tuple<typename traits::acquire_future<T>::type...> 237 result_type; 238 typedef detail::async_when_all_frame<result_type> frame_type; 239 240 traits::acquire_future_disp func; 241 242 typename frame_type::base_type::init_no_addref no_addref; 243 244 auto frame = util::traverse_pack_async_allocator( 245 util::internal_allocator<>{}, 246 util::async_traverse_in_place_tag<frame_type>{}, no_addref, 247 func(std::forward<T>(args))...); 248 249 using traits::future_access; 250 return future_access<typename frame_type::type>::create( 251 std::move(frame)); 252 } 253 } 254 255 template <typename First, typename Second> when_all(First && first,Second && second)256 auto when_all(First&& first, Second&& second) 257 -> decltype(detail::when_all_impl( 258 std::forward<First>(first), std::forward<Second>(second))) 259 { 260 return detail::when_all_impl( 261 std::forward<First>(first), std::forward<Second>(second)); 262 } 263 template <typename Iterator, 264 typename Container = std::vector< 265 typename detail::future_iterator_traits<Iterator>::type>> when_all(Iterator begin,Iterator end)266 future<Container> when_all(Iterator begin, Iterator end) 267 { 268 return detail::when_all_impl( 269 detail::acquire_future_iterators<Iterator, Container>(begin, end)); 270 } 271 272 inline lcos::future<util::tuple<> > //-V524 when_all()273 when_all() 274 { 275 typedef util::tuple<> result_type; 276 return lcos::make_ready_future(result_type()); 277 } 278 279 /////////////////////////////////////////////////////////////////////////// 280 template <typename Iterator, 281 typename Container = std::vector< 282 typename lcos::detail::future_iterator_traits<Iterator>::type>> when_all_n(Iterator begin,std::size_t count)283 lcos::future<Container> when_all_n(Iterator begin, std::size_t count) 284 { 285 return detail::when_all_impl( 286 detail::acquire_future_n<Iterator, Container>(begin, count)); 287 } 288 289 /////////////////////////////////////////////////////////////////////////// 290 template <typename... Args, 291 typename std::enable_if<(sizeof...(Args) == 1U) || 292 (sizeof...(Args) > 2U)>::type* = nullptr> when_all(Args &&...args)293 auto when_all(Args&&... args) 294 -> decltype(detail::when_all_impl(std::forward<Args>(args)...)) 295 { 296 return detail::when_all_impl(std::forward<Args>(args)...); 297 } 298 }} 299 300 namespace hpx 301 { 302 using lcos::when_all; 303 using lcos::when_all_n; 304 } 305 306 #endif // DOXYGEN 307 #endif 308