Line | % of fetches | Source |
---|---|---|
1 | // Copyright (c) 2007-2015 Hartmut Kaiser | |
2 | // Copyright (c) 2013 Agustin Berge | |
3 | // | |
4 | // Distributed under the Boost Software License, Version 1.0. (See accompanying | |
5 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | |
6 | ||
7 | /// \file lcos/when_all.hpp | |
8 | ||
9 | #if !defined(HPX_LCOS_WHEN_ALL_APR_19_2012_1140AM) | |
10 | #define HPX_LCOS_WHEN_ALL_APR_19_2012_1140AM | |
11 | ||
12 | #if defined(DOXYGEN) | |
13 | namespace hpx | |
14 | { | |
15 | /// The function \a when_all is an operator allowing to join on the result | |
16 | /// of all given futures. It AND-composes all future objects given and | |
17 | /// returns a new future object representing the same list of futures | |
18 | /// after they finished executing. | |
19 | /// | |
20 | /// \param first [in] The iterator pointing to the first element of a | |
21 | /// sequence of \a future or \a shared_future objects for | |
22 | /// which \a when_all should wait. | |
23 | /// \param last [in] The iterator pointing to the last element of a | |
24 | /// sequence of \a future or \a shared_future objects for | |
25 | /// which \a when_all should wait. | |
26 | /// | |
27 | /// \return Returns a future holding the same list of futures as has | |
28 | /// been passed to \a when_all. | |
29 | /// - future<Container<future<R>>>: If the input cardinality is | |
30 | /// unknown at compile time and the futures are all of the | |
31 | /// same type. The order of the futures in the output container | |
32 | /// will be the same as given by the input iterator. | |
33 | /// | |
34 | /// \note Calling this version of \a when_all where first == last, returns | |
35 | /// a future with an empty container that is immediately ready. | |
36 | /// Each future and shared_future is waited upon and then copied into | |
37 | /// the collection of the output (returned) future, maintaining the | |
38 | /// order of the futures in the input collection. | |
39 | /// The future returned by \a when_all will not throw an exception, | |
40 | /// but the futures held in the output collection may. | |
41 | template <typename InputIter, typename Container = | |
42 | vector<future<typename std::iterator_traits<InputIter>::value_type>>> | |
43 | future<Container> | |
44 | when_all(InputIter first, InputIter last); | |
45 | ||
46 | /// The function \a when_all is an operator allowing to join on the result | |
47 | /// of all given futures. It AND-composes all future objects given and | |
48 | /// returns a new future object representing the same list of futures | |
49 | /// after they finished executing. | |
50 | /// | |
51 | /// \param values [in] A range holding an arbitrary amount of \a future | |
52 | /// or \a shared_future objects for which \a when_all | |
53 | /// should wait. | |
54 | /// | |
55 | /// \return Returns a future holding the same list of futures as has | |
56 | /// been passed to when_all. | |
57 | /// - future<Container<future<R>>>: If the input cardinality is | |
58 | /// unknown at compile time and the futures are all of the | |
59 | /// same type. | |
60 | /// | |
61 | /// \note Calling this version of \a when_all where the input container is | |
62 | /// empty, returns a future with an empty container that is immediately | |
63 | /// ready. | |
64 | /// Each future and shared_future is waited upon and then copied into | |
65 | /// the collection of the output (returned) future, maintaining the | |
66 | /// order of the futures in the input collection. | |
67 | /// The future returned by \a when_all will not throw an exception, | |
68 | /// but the futures held in the output collection may. | |
69 | template <typename Range> | |
70 | future<Range> | |
71 | when_all(Range&& values); | |
72 | ||
73 | /// The function \a when_all is an operator allowing to join on the result | |
74 | /// of all given futures. It AND-composes all future objects given and | |
75 | /// returns a new future object representing the same list of futures | |
76 | /// after they finished executing. | |
77 | /// | |
78 | /// \param futures [in] An arbitrary number of \a future or \a shared_future | |
79 | /// objects, possibly holding different types for which | |
80 | /// \a when_all should wait. | |
81 | /// | |
82 | /// \return Returns a future holding the same list of futures as has | |
83 | /// been passed to \a when_all. | |
84 | /// - future<tuple<future<T0>, future<T1>, future<T2>...>>: If | |
85 | /// inputs are fixed in number and are of heterogeneous types. | |
86 | /// The inputs can be any arbitrary number of future objects. | |
87 | /// - future<tuple<>> if \a when_all is called with zero arguments. | |
88 | /// The returned future will be initially ready. | |
89 | /// | |
90 | /// \note Each future and shared_future is waited upon and then copied into | |
91 | /// the collection of the output (returned) future, maintaining the | |
92 | /// order of the futures in the input collection. | |
93 | /// The future returned by \a when_all will not throw an exception, | |
94 | /// but the futures held in the output collection may. | |
95 | template <typename ...T> | |
96 | future<tuple<future<T>...>> | |
97 | when_all(T &&... futures); | |
98 | ||
99 | /// The function \a when_all_n is an operator allowing to join on the result | |
100 | /// of all given futures. It AND-composes all future objects given and | |
101 | /// returns a new future object representing the same list of futures | |
102 | /// after they finished executing. | |
103 | /// | |
104 | /// \param begin [in] The iterator pointing to the first element of a | |
105 | /// sequence of \a future or \a shared_future objects for | |
106 | /// which \a wait_all_n should wait. | |
107 | /// \param count [in] The number of elements in the sequence starting at | |
108 | /// \a first. | |
109 | /// | |
110 | /// \return Returns a future holding the same list of futures as has | |
111 | /// been passed to \a when_all_n. | |
112 | /// - future<Container<future<R>>>: If the input cardinality is | |
113 | /// unknown at compile time and the futures are all of the | |
114 | /// same type. The order of the futures in the output vector | |
115 | /// will be the same as given by the input iterator. | |
116 | /// | |
117 | /// \throws This function will throw errors which are encountered while | |
118 | /// setting up the requested operation only. Errors encountered | |
119 | /// while executing the operations delivering the results to be | |
120 | /// stored in the futures are reported through the futures | |
121 | /// themselves. | |
122 | /// | |
123 | /// \note As long as \a ec is not pre-initialized to \a hpx::throws this | |
124 | /// function doesn't throw but returns the result code using the | |
125 | /// parameter \a ec. Otherwise it throws an instance of | |
126 | /// hpx::exception. | |
127 | /// | |
128 | /// \note None of the futures in the input sequence are invalidated. | |
129 | template <typename InputIter, typename Container = | |
130 | vector<future<typename std::iterator_traits<InputIter>::value_type>>> | |
131 | future<Container> | |
132 | when_all_n(InputIter begin, std::size_t count); | |
133 | } | |
134 | ||
135 | #else // DOXYGEN | |
136 | ||
137 | #include <hpx/config.hpp> | |
138 | #include <hpx/lcos/detail/future_data.hpp> | |
139 | #include <hpx/lcos/future.hpp> | |
140 | #include <hpx/lcos/when_some.hpp> | |
141 | #include <hpx/traits/acquire_future.hpp> | |
142 | #include <hpx/traits/acquire_shared_state.hpp> | |
143 | #include <hpx/traits/future_access.hpp> | |
144 | #include <hpx/traits/is_future.hpp> | |
145 | #include <hpx/traits/is_future_range.hpp> | |
146 | #include <hpx/util/decay.hpp> | |
147 | #include <hpx/util/deferred_call.hpp> | |
148 | #include <hpx/util/tuple.hpp> | |
149 | ||
150 | #include <boost/intrusive_ptr.hpp> | |
151 | #include <boost/range/functions.hpp> | |
152 | ||
153 | #include <algorithm> | |
154 | #include <cstddef> | |
155 | #include <iterator> | |
156 | #include <type_traits> | |
157 | #include <utility> | |
158 | #include <vector> | |
159 | ||
160 | /////////////////////////////////////////////////////////////////////////////// | |
161 | namespace hpx { namespace lcos | |
162 | { | |
163 | namespace detail | |
164 | { | |
165 | /////////////////////////////////////////////////////////////////////// | |
166 | template <typename T, typename Enable = void> | |
167 | struct when_all_result | |
168 | { | |
169 | typedef T type; | |
170 | ||
171 | static type call(T&& t) | |
172 | { | |
173 | return std::move(t); | |
174 | } | |
175 | }; | |
176 | ||
177 | template <typename T> | |
178 | struct when_all_result<util::tuple<T>, | |
179 | typename std::enable_if< | |
180 | traits::is_future_range<T>::value | |
181 | >::type> | |
182 | { | |
183 | typedef T type; | |
184 | ||
185 | static type call(util::tuple<T>&& t) | |
186 | { | |
187 | return std::move(util::get<0>(t)); | |
188 | } | |
189 | }; | |
190 | ||
191 | /////////////////////////////////////////////////////////////////////// | |
192 | template <typename Tuple> | |
193 | struct when_all_frame //-V690 | |
194 | : hpx::lcos::detail::future_data<typename when_all_result<Tuple>::type> | |
195 | { | |
196 | typedef typename when_all_result<Tuple>::type result_type; | |
197 | typedef hpx::lcos::future<result_type> type; | |
198 | ||
199 | private: | |
200 | // workaround gcc regression wrongly instantiating constructors | |
201 | when_all_frame(); | |
202 | when_all_frame(when_all_frame const&); | |
203 | ||
204 | template <std::size_t I> | |
205 | struct is_end | |
206 | : std::integral_constant< | |
207 | bool, | |
208 | util::tuple_size<Tuple>::value == I | |
209 | > | |
210 | {}; | |
211 | ||
212 | public: | |
213 | template <typename Tuple_> | |
214 | when_all_frame(Tuple_&& t) | |
215 | : t_(std::forward<Tuple_>(t)) | |
216 | {} | |
217 | ||
218 | protected: | |
219 | // End of the tuple is reached | |
220 | template <std::size_t I> | |
221 | HPX_FORCEINLINE | |
222 | void do_await(std::true_type) | |
223 | { | |
224 | this->set_value(when_all_result<Tuple>::call(std::move(t_))); | |
225 | } | |
226 | ||
227 | // Current element is a range of futures | |
228 | template <std::size_t I, typename Iter> | |
229 | void await_range(Iter next, Iter end) | |
230 | { | |
231 | typedef typename std::iterator_traits<Iter>::value_type | |
232 | future_type; | |
233 | typedef typename traits::future_traits<future_type>::type | |
234 | future_result_type; | |
235 | ||
236 | void (when_all_frame::*f)(Iter, Iter) = | |
237 | &when_all_frame::await_range<I>; | |
238 | ||
239 | for (/**/; next != end; ++next) | |
240 | { | |
241 | boost::intrusive_ptr< | |
242 | lcos::detail::future_data<future_result_type> | |
243 | > next_future_data = | |
244 | traits::detail::get_shared_state(*next); | |
245 | ||
246 | if (!next_future_data->is_ready()) | |
247 | { | |
248 | next_future_data->execute_deferred(); | |
249 | ||
250 | // execute_deferred might have made the future ready | |
251 | if (!next_future_data->is_ready()) | |
252 | { | |
253 | // Attach a continuation to this future which will | |
254 | // re-evaluate it and continue to the next element | |
255 | // in the sequence (if any). | |
256 | boost::intrusive_ptr<when_all_frame> this_(this); | |
257 | next_future_data->set_on_completed(util::deferred_call( | |
258 | f, std::move(this_), | |
259 | std::move(next), std::move(end))); | |
260 | return; | |
261 | } | |
262 | } | |
263 | } | |
264 | ||
265 | do_await<I + 1>(is_end<I + 1>()); | |
266 | } | |
267 | ||
268 | template <std::size_t I> | |
269 | HPX_FORCEINLINE | |
270 | void await_next(std::false_type, std::true_type) | |
271 | { | |
272 | await_range<I>( | |
273 | boost::begin(boost::unwrap_ref(util::get<I>(t_))), | |
274 | boost::end(boost::unwrap_ref(util::get<I>(t_)))); | |
275 | } | |
276 | ||
277 | // Current element is a simple future | |
278 | template <std::size_t I> | |
279 | HPX_FORCEINLINE | |
280 | void await_next(std::true_type, std::false_type) | |
281 | { | |
282 | typedef typename util::decay_unwrap< | |
283 | typename util::tuple_element<I, Tuple>::type | |
284 | >::type future_type; | |
285 | ||
286 | future_type& f_ = util::get<I>(t_); | |
287 | ||
288 | typedef typename traits::future_traits<future_type>::type | |
289 | future_result_type; | |
290 | ||
291 | boost::intrusive_ptr< | |
292 | lcos::detail::future_data<future_result_type> | |
293 | > next_future_data = | |
294 | traits::detail::get_shared_state(f_); | |
295 | ||
296 | if (!next_future_data->is_ready()) | |
297 | { | |
298 | next_future_data->execute_deferred(); | |
299 | ||
300 | // execute_deferred might have made the future ready | |
301 | if (!next_future_data->is_ready()) | |
302 | { | |
303 | // Attach a continuation to this future which will | |
304 | // re-evaluate it and continue to the next argument | |
305 | // (if any). | |
306 | void (when_all_frame::*f)(std::true_type, std::false_type) = | |
307 | &when_all_frame::await_next<I>; | |
308 | ||
309 | boost::intrusive_ptr<when_all_frame> this_(this); | |
310 | next_future_data->set_on_completed(util::deferred_call( | |
311 | f, std::move(this_), std::true_type(), std::false_type())); | |
312 | return; | |
313 | } | |
314 | } | |
315 | ||
316 | do_await<I + 1>(is_end<I + 1>()); | |
317 | } | |
318 | ||
319 | template <std::size_t I> | |
320 | HPX_FORCEINLINE | |
321 | void do_await(std::false_type) | |
322 | { | |
323 | typedef typename util::decay_unwrap< | |
324 | typename util::tuple_element<I, Tuple>::type | |
325 | >::type future_type; | |
326 | ||
327 | typedef traits::is_future<future_type> is_future; | |
328 | typedef traits::is_future_range<future_type> is_range; | |
329 | ||
330 | await_next<I>(is_future(), is_range()); | |
331 | } | |
332 | ||
333 | public: | |
334 | HPX_FORCEINLINE void do_await() | |
335 | { | |
336 | do_await<0>(is_end<0>()); | |
337 | } | |
338 | ||
339 | private: | |
340 | Tuple t_; | |
341 | }; | |
342 | } | |
343 | ||
344 | /////////////////////////////////////////////////////////////////////////// | |
345 | template <typename Range> | |
346 | typename std::enable_if<traits::is_future_range<Range>::value, | |
347 | lcos::future<typename std::decay<Range>::type> >::type //-V659 | |
348 | when_all(Range&& values) | |
349 | { | |
350 | typedef detail::when_all_frame< | |
351 | util::tuple<Range> | |
352 | > frame_type; | |
353 | ||
354 | boost::intrusive_ptr<frame_type> p(new frame_type( | |
355 | util::forward_as_tuple(std::move(values)))); | |
356 | p->do_await(); | |
357 | ||
358 | using traits::future_access; | |
359 | return future_access<typename frame_type::type>::create(std::move(p)); | |
360 | } | |
361 | ||
362 | template <typename Range> | |
363 | typename std::enable_if<traits::is_future_range<Range>::value, | |
364 | lcos::future<typename std::decay<Range>::type> >::type | |
365 | when_all(Range& values) | |
366 | { | |
367 | Range values_ = traits::acquire_future<Range>()(values); | |
368 | return lcos::when_all(std::move(values_)); | |
369 | } | |
370 | ||
371 | template <typename Iterator, typename Container = | |
372 | std::vector<typename lcos::detail::future_iterator_traits<Iterator>::type> > | |
373 | lcos::future<Container> | |
374 | when_all(Iterator begin, Iterator end) | |
375 | { | |
376 | Container values; | |
377 | ||
378 | typename std::iterator_traits<Iterator>:: | |
379 | difference_type difference = std::distance(begin, end); | |
380 | if (difference > 0) | |
381 | traits::detail::reserve_if_vector( | |
382 | values, static_cast<std::size_t>(difference)); | |
383 | ||
384 | std::transform(begin, end, std::back_inserter(values), | |
385 | traits::acquire_future_disp()); | |
386 | ||
387 | return lcos::when_all(std::move(values)); | |
388 | } | |
389 | ||
390 | inline lcos::future<util::tuple<> > //-V524 | |
391 | when_all() | |
392 | { | |
393 | typedef util::tuple<> result_type; | |
394 | return lcos::make_ready_future(result_type()); | |
395 | } | |
396 | ||
397 | /////////////////////////////////////////////////////////////////////////// | |
398 | template <typename Iterator, typename Container = | |
399 | std::vector<typename lcos::detail::future_iterator_traits<Iterator>::type> > | |
400 | lcos::future<Container> | |
401 | when_all_n(Iterator begin, std::size_t count) | |
402 | { | |
403 | Container values; | |
404 | traits::detail::reserve_if_vector(values, count); | |
405 | ||
406 | traits::acquire_future_disp func; | |
407 | for (std::size_t i = 0; i != count; ++i) | |
408 | values.push_back(func(*begin++)); | |
409 | ||
410 | return lcos::when_all(std::move(values)); | |
411 | } | |
412 | ||
413 | /////////////////////////////////////////////////////////////////////////// | |
414 | template <typename... Ts> | |
415 | typename detail::when_all_frame< | |
416 | util::tuple<typename traits::acquire_future<Ts>::type...> | |
417 | >::type | |
418 | when_all(Ts&&... ts) | |
419 | { | |
420 | typedef util::tuple< | |
421 | typename traits::acquire_future<Ts>::type... | |
422 | > result_type; | |
423 | typedef detail::when_all_frame<result_type> frame_type; | |
424 | ||
425 | traits::acquire_future_disp func; | |
426 | result_type values(func(std::forward<Ts>(ts))...); | |
427 | ||
428 | boost::intrusive_ptr<frame_type> p(new frame_type(std::move(values))); | |
429 | p->do_await(); | |
430 | ||
431 | using traits::future_access; | |
432 | return future_access<typename frame_type::type>::create(std::move(p)); | |
433 | } | |
434 | }} | |
435 | ||
436 | namespace hpx | |
437 | { | |
438 | using lcos::when_all; | |
439 | using lcos::when_all_n; | |
440 | } | |
441 | ||
442 | #endif // DOXYGEN | |
443 | #endif | |
444 |
Copyright (c) 2006-2012 Rogue Wave Software, Inc. All Rights Reserved.
Patents pending.