fix doc example typo
[boost.git] / boost / mpi / nonblocking.hpp
blobba024c7df425060ece26a843e1a68c9cfcf821dc
1 // Copyright (C) 2006 Douglas Gregor <doug.gregor -at- gmail.com>.
3 // Use, modification and distribution is subject to the Boost Software
4 // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
5 // http://www.boost.org/LICENSE_1_0.txt)
7 /** @file nonblocking.hpp
9 * This header defines operations for completing non-blocking
10 * communication requests.
12 #ifndef BOOST_MPI_NONBLOCKING_HPP
13 #define BOOST_MPI_NONBLOCKING_HPP
15 #include <boost/mpi/config.hpp>
16 #include <vector>
17 #include <iterator> // for std::iterator_traits
18 #include <boost/optional.hpp>
19 #include <utility> // for std::pair
20 #include <algorithm> // for iter_swap, reverse
21 #include <boost/static_assert.hpp>
22 #include <boost/mpi/request.hpp>
23 #include <boost/mpi/status.hpp>
24 #include <boost/mpi/exception.hpp>
26 namespace boost { namespace mpi {
28 /**
29 * @brief Wait until any non-blocking request has completed.
31 * This routine takes in a set of requests stored in the iterator
32 * range @c [first,last) and waits until any of these requests has
33 * been completed. It provides functionality equivalent to
34 * @c MPI_Waitany.
36 * @param first The iterator that denotes the beginning of the
37 * sequence of request objects.
39 * @param last The iterator that denotes the end of the sequence of
40 * request objects. This may not be equal to @c first.
42 * @returns A pair containing the status object that corresponds to
43 * the completed operation and the iterator referencing the completed
44 * request.
46 template<typename ForwardIterator>
47 std::pair<status, ForwardIterator>
48 wait_any(ForwardIterator first, ForwardIterator last)
50 using std::advance;
52 BOOST_ASSERT(first != last);
54 typedef typename std::iterator_traits<ForwardIterator>::difference_type
55 difference_type;
57 bool all_trivial_requests = true;
58 difference_type n = 0;
59 ForwardIterator current = first;
60 while (true) {
61 // Check if we have found a completed request. If so, return it.
62 if (optional<status> result = current->test())
63 return std::make_pair(*result, current);
65 // Check if this request (and all others before it) are "trivial"
66 // requests, e.g., they can be represented with a single
67 // MPI_Request.
68 all_trivial_requests =
69 all_trivial_requests
70 && !current->m_handler
71 && current->m_requests[1] == MPI_REQUEST_NULL;
73 // Move to the next request.
74 ++n;
75 if (++current == last) {
76 // We have reached the end of the list. If all requests thus far
77 // have been trivial, we can call MPI_Waitany directly, because
78 // it may be more efficient than our busy-wait semantics.
79 if (all_trivial_requests) {
80 std::vector<MPI_Request> requests;
81 requests.reserve(n);
82 for (current = first; current != last; ++current)
83 requests.push_back(current->m_requests[0]);
85 // Let MPI wait until one of these operations completes.
86 int index;
87 status stat;
88 BOOST_MPI_CHECK_RESULT(MPI_Waitany,
89 (n, &requests[0], &index, &stat.m_status));
91 // We don't have a notion of empty requests or status objects,
92 // so this is an error.
93 if (index == MPI_UNDEFINED)
94 boost::throw_exception(exception("MPI_Waitany", MPI_ERR_REQUEST));
96 // Find the iterator corresponding to the completed request.
97 current = first;
98 advance(current, index);
99 current->m_requests[0] = requests[index];
100 return std::make_pair(stat, current);
103 // There are some nontrivial requests, so we must continue our
104 // busy waiting loop.
105 n = 0;
106 current = first;
107 all_trivial_requests = true;
111 // We cannot ever get here
112 BOOST_ASSERT(false);
115 /**
116 * @brief Test whether any non-blocking request has completed.
118 * This routine takes in a set of requests stored in the iterator
119 * range @c [first,last) and tests whether any of these requests has
120 * been completed. This routine is similar to @c wait_any, but will
121 * not block waiting for requests to completed. It provides
122 * functionality equivalent to @c MPI_Testany.
124 * @param first The iterator that denotes the beginning of the
125 * sequence of request objects.
127 * @param last The iterator that denotes the end of the sequence of
128 * request objects.
130 * @returns If any outstanding requests have completed, a pair
131 * containing the status object that corresponds to the completed
132 * operation and the iterator referencing the completed
133 * request. Otherwise, an empty @c optional<>.
135 template<typename ForwardIterator>
136 optional<std::pair<status, ForwardIterator> >
137 test_any(ForwardIterator first, ForwardIterator last)
139 for (ForwardIterator current = first; first != last; ++first) {
140 // Check if we have found a completed request. If so, return it.
141 if (optional<status> result = current->test())
142 return std::make_pair(*result, current);
145 // We found nothing
146 return optional<std::pair<status, ForwardIterator> >();
149 /**
150 * @brief Wait until all non-blocking requests have completed.
152 * This routine takes in a set of requests stored in the iterator
153 * range @c [first,last) and waits until all of these requests have
154 * been completed. It provides functionality equivalent to
155 * @c MPI_Waitall.
157 * @param first The iterator that denotes the beginning of the
158 * sequence of request objects.
160 * @param last The iterator that denotes the end of the sequence of
161 * request objects.
163 * @param out If provided, an output iterator through which the
164 * status of each request will be emitted. The @c status objects are
165 * emitted in the same order as the requests are retrieved from
166 * @c [first,last).
168 * @returns If an @p out parameter was provided, the value @c out
169 * after all of the @c status objects have been emitted.
171 template<typename ForwardIterator, typename OutputIterator>
172 OutputIterator
173 wait_all(ForwardIterator first, ForwardIterator last, OutputIterator out)
175 typedef typename std::iterator_traits<ForwardIterator>::difference_type
176 difference_type;
178 using std::distance;
180 difference_type num_outstanding_requests = distance(first, last);
182 std::vector<status> results(num_outstanding_requests);
183 std::vector<bool> completed(num_outstanding_requests);
185 while (num_outstanding_requests > 0) {
186 bool all_trivial_requests = true;
187 difference_type idx = 0;
188 for (ForwardIterator current = first; current != last; ++current, ++idx) {
189 if (!completed[idx]) {
190 if (optional<status> stat = current->test()) {
191 // This outstanding request has been completed. We're done.
192 results[idx] = *stat;
193 completed[idx] = true;
194 --num_outstanding_requests;
195 all_trivial_requests = false;
196 } else {
197 // Check if this request (and all others before it) are "trivial"
198 // requests, e.g., they can be represented with a single
199 // MPI_Request.
200 all_trivial_requests =
201 all_trivial_requests
202 && !current->m_handler
203 && current->m_requests[1] == MPI_REQUEST_NULL;
208 // If we have yet to fulfill any requests and all of the requests
209 // are trivial (i.e., require only a single MPI_Request to be
210 // fulfilled), call MPI_Waitall directly.
211 if (all_trivial_requests
212 && num_outstanding_requests == (difference_type)results.size()) {
213 std::vector<MPI_Request> requests;
214 requests.reserve(num_outstanding_requests);
215 for (ForwardIterator current = first; current != last; ++current)
216 requests.push_back(current->m_requests[0]);
218 // Let MPI wait until all of these operations completes.
219 std::vector<MPI_Status> stats(num_outstanding_requests);
220 BOOST_MPI_CHECK_RESULT(MPI_Waitall,
221 (num_outstanding_requests, &requests[0],
222 &stats[0]));
224 for (std::vector<MPI_Status>::iterator i = stats.begin();
225 i != stats.end(); ++i, ++out) {
226 status stat;
227 stat.m_status = *i;
228 *out = stat;
231 return out;
234 all_trivial_requests = false;
237 return std::copy(results.begin(), results.end(), out);
241 * \overload
243 template<typename ForwardIterator>
244 void
245 wait_all(ForwardIterator first, ForwardIterator last)
247 typedef typename std::iterator_traits<ForwardIterator>::difference_type
248 difference_type;
250 using std::distance;
252 difference_type num_outstanding_requests = distance(first, last);
254 std::vector<bool> completed(num_outstanding_requests);
256 while (num_outstanding_requests > 0) {
257 bool all_trivial_requests = true;
259 difference_type idx = 0;
260 for (ForwardIterator current = first; current != last; ++current, ++idx) {
261 if (!completed[idx]) {
262 if (optional<status> stat = current->test()) {
263 // This outstanding request has been completed.
264 completed[idx] = true;
265 --num_outstanding_requests;
266 all_trivial_requests = false;
267 } else {
268 // Check if this request (and all others before it) are "trivial"
269 // requests, e.g., they can be represented with a single
270 // MPI_Request.
271 all_trivial_requests =
272 all_trivial_requests
273 && !current->m_handler
274 && current->m_requests[1] == MPI_REQUEST_NULL;
279 // If we have yet to fulfill any requests and all of the requests
280 // are trivial (i.e., require only a single MPI_Request to be
281 // fulfilled), call MPI_Waitall directly.
282 if (all_trivial_requests
283 && num_outstanding_requests == (difference_type)completed.size()) {
284 std::vector<MPI_Request> requests;
285 requests.reserve(num_outstanding_requests);
286 for (ForwardIterator current = first; current != last; ++current)
287 requests.push_back(current->m_requests[0]);
289 // Let MPI wait until all of these operations completes.
290 BOOST_MPI_CHECK_RESULT(MPI_Waitall,
291 (num_outstanding_requests, &requests[0],
292 MPI_STATUSES_IGNORE));
294 // Signal completion
295 num_outstanding_requests = 0;
300 /**
301 * @brief Tests whether all non-blocking requests have completed.
303 * This routine takes in a set of requests stored in the iterator
304 * range @c [first,last) and determines whether all of these requests
305 * have been completed. However, due to limitations of the underlying
306 * MPI implementation, if any of the requests refers to a
307 * non-blocking send or receive of a serialized data type, @c
308 * test_all will always return the equivalent of @c false (i.e., the
309 * requests cannot all be finished at this time). This routine
310 * performs the same functionality as @c wait_all, except that this
311 * routine will not block. This routine provides functionality
312 * equivalent to @c MPI_Testall.
314 * @param first The iterator that denotes the beginning of the
315 * sequence of request objects.
317 * @param last The iterator that denotes the end of the sequence of
318 * request objects.
320 * @param out If provided and all requests hav been completed, an
321 * output iterator through which the status of each request will be
322 * emitted. The @c status objects are emitted in the same order as
323 * the requests are retrieved from @c [first,last).
325 * @returns If an @p out parameter was provided, the value @c out
326 * after all of the @c status objects have been emitted (if all
327 * requests were completed) or an empty @c optional<>. If no @p out
328 * parameter was provided, returns @c true if all requests have
329 * completed or @c false otherwise.
331 template<typename ForwardIterator, typename OutputIterator>
332 optional<OutputIterator>
333 test_all(ForwardIterator first, ForwardIterator last, OutputIterator out)
335 std::vector<MPI_Request> requests;
336 for (; first != last; ++first) {
337 // If we have a non-trivial request, then no requests can be
338 // completed.
339 if (first->m_handler || first->m_requests[1] != MPI_REQUEST_NULL)
340 return optional<OutputIterator>();
342 requests.push_back(first->m_requests[0]);
345 int flag = 0;
346 int n = requests.size();
347 std::vector<MPI_Status> stats(n);
348 BOOST_MPI_CHECK_RESULT(MPI_Testall, (n, &requests[0], &flag, &stats[0]));
349 if (flag) {
350 for (int i = 0; i < n; ++i, ++out) {
351 status stat;
352 stat.m_status = stats[i];
353 *out = stat;
355 return out;
356 } else {
357 return optional<OutputIterator>();
362 * \overload
364 template<typename ForwardIterator>
365 bool
366 test_all(ForwardIterator first, ForwardIterator last)
368 std::vector<MPI_Request> requests;
369 for (; first != last; ++first) {
370 // If we have a non-trivial request, then no requests can be
371 // completed.
372 if (first->m_handler || first->m_requests[1] != MPI_REQUEST_NULL)
373 return false;
375 requests.push_back(first->m_requests[0]);
378 int flag = 0;
379 int n = requests.size();
380 BOOST_MPI_CHECK_RESULT(MPI_Testall,
381 (n, &requests[0], &flag, MPI_STATUSES_IGNORE));
382 return flag != 0;
385 /**
386 * @brief Wait until some non-blocking requests have completed.
388 * This routine takes in a set of requests stored in the iterator
389 * range @c [first,last) and waits until at least one of the requests
390 * has completed. It then completes all of the requests it can,
391 * partitioning the input sequence into pending requests followed by
392 * completed requests. If an output iterator is provided, @c status
393 * objects will be emitted for each of the completed requests. This
394 * routine provides functionality equivalent to @c MPI_Waitsome.
396 * @param first The iterator that denotes the beginning of the
397 * sequence of request objects.
399 * @param last The iterator that denotes the end of the sequence of
400 * request objects. This may not be equal to @c first.
402 * @param out If provided, the @c status objects corresponding to
403 * completed requests will be emitted through this output iterator.
405 * @returns If the @p out parameter was provided, a pair containing
406 * the output iterator @p out after all of the @c status objects have
407 * been written through it and an iterator referencing the first
408 * completed request. If no @p out parameter was provided, only the
409 * iterator referencing the first completed request will be emitted.
411 template<typename BidirectionalIterator, typename OutputIterator>
412 std::pair<OutputIterator, BidirectionalIterator>
413 wait_some(BidirectionalIterator first, BidirectionalIterator last,
414 OutputIterator out)
416 using std::advance;
418 if (first == last)
419 return std::make_pair(out, first);
421 typedef typename std::iterator_traits<BidirectionalIterator>::difference_type
422 difference_type;
424 bool all_trivial_requests = true;
425 difference_type n = 0;
426 BidirectionalIterator current = first;
427 BidirectionalIterator start_of_completed = last;
428 while (true) {
429 // Check if we have found a completed request.
430 if (optional<status> result = current->test()) {
431 using std::iter_swap;
433 // Emit the resulting status object
434 *out++ = *result;
436 // We're expanding the set of completed requests
437 --start_of_completed;
439 if (current == start_of_completed) {
440 // If we have hit the end of the list of pending
441 // requests. Finish up by fixing the order of the completed
442 // set to match the order in which we emitted status objects,
443 // then return.
444 std::reverse(start_of_completed, last);
445 return std::make_pair(out, start_of_completed);
448 // Swap the request we just completed with the last request that
449 // has not yet been tested.
450 iter_swap(current, start_of_completed);
452 continue;
455 // Check if this request (and all others before it) are "trivial"
456 // requests, e.g., they can be represented with a single
457 // MPI_Request.
458 all_trivial_requests =
459 all_trivial_requests
460 && !current->m_handler
461 && current->m_requests[1] == MPI_REQUEST_NULL;
463 // Move to the next request.
464 ++n;
465 if (++current == start_of_completed) {
466 if (start_of_completed != last) {
467 // We have satisfied some requests. Make the order of the
468 // completed requests match that of the status objects we've
469 // already emitted and we're done.
470 std::reverse(start_of_completed, last);
471 return std::make_pair(out, start_of_completed);
474 // We have reached the end of the list. If all requests thus far
475 // have been trivial, we can call MPI_Waitsome directly, because
476 // it may be more efficient than our busy-wait semantics.
477 if (all_trivial_requests) {
478 std::vector<MPI_Request> requests;
479 std::vector<int> indices(n);
480 std::vector<MPI_Status> stats(n);
481 requests.reserve(n);
482 for (current = first; current != last; ++current)
483 requests.push_back(current->m_requests[0]);
485 // Let MPI wait until some of these operations complete.
486 int num_completed;
487 BOOST_MPI_CHECK_RESULT(MPI_Waitsome,
488 (n, &requests[0], &num_completed, &indices[0],
489 &stats[0]));
491 // Translate the index-based result of MPI_Waitsome into a
492 // partitioning on the requests.
493 int current_offset = 0;
494 current = first;
495 for (int index = 0; index < num_completed; ++index, ++out) {
496 using std::iter_swap;
498 // Move "current" to the request object at this index
499 advance(current, indices[index] - current_offset);
500 current_offset = indices[index];
502 // Emit the status object
503 status stat;
504 stat.m_status = stats[index];
505 *out = stat;
507 // Finish up the request and swap it into the "completed
508 // requests" partition.
509 current->m_requests[0] = requests[indices[index]];
510 --start_of_completed;
511 iter_swap(current, start_of_completed);
514 // We have satisfied some requests. Make the order of the
515 // completed requests match that of the status objects we've
516 // already emitted and we're done.
517 std::reverse(start_of_completed, last);
518 return std::make_pair(out, start_of_completed);
521 // There are some nontrivial requests, so we must continue our
522 // busy waiting loop.
523 n = 0;
524 current = first;
528 // We cannot ever get here
529 BOOST_ASSERT(false);
533 * \overload
535 template<typename BidirectionalIterator>
536 BidirectionalIterator
537 wait_some(BidirectionalIterator first, BidirectionalIterator last)
539 using std::advance;
541 if (first == last)
542 return first;
544 typedef typename std::iterator_traits<BidirectionalIterator>::difference_type
545 difference_type;
547 bool all_trivial_requests = true;
548 difference_type n = 0;
549 BidirectionalIterator current = first;
550 BidirectionalIterator start_of_completed = last;
551 while (true) {
552 // Check if we have found a completed request.
553 if (optional<status> result = current->test()) {
554 using std::iter_swap;
556 // We're expanding the set of completed requests
557 --start_of_completed;
559 // If we have hit the end of the list of pending requests, we're
560 // done.
561 if (current == start_of_completed)
562 return start_of_completed;
564 // Swap the request we just completed with the last request that
565 // has not yet been tested.
566 iter_swap(current, start_of_completed);
568 continue;
571 // Check if this request (and all others before it) are "trivial"
572 // requests, e.g., they can be represented with a single
573 // MPI_Request.
574 all_trivial_requests =
575 all_trivial_requests
576 && !current->m_handler
577 && current->m_requests[1] == MPI_REQUEST_NULL;
579 // Move to the next request.
580 ++n;
581 if (++current == start_of_completed) {
582 // If we have satisfied some requests, we're done.
583 if (start_of_completed != last)
584 return start_of_completed;
586 // We have reached the end of the list. If all requests thus far
587 // have been trivial, we can call MPI_Waitsome directly, because
588 // it may be more efficient than our busy-wait semantics.
589 if (all_trivial_requests) {
590 std::vector<MPI_Request> requests;
591 std::vector<int> indices(n);
592 requests.reserve(n);
593 for (current = first; current != last; ++current)
594 requests.push_back(current->m_requests[0]);
596 // Let MPI wait until some of these operations complete.
597 int num_completed;
598 BOOST_MPI_CHECK_RESULT(MPI_Waitsome,
599 (n, &requests[0], &num_completed, &indices[0],
600 MPI_STATUSES_IGNORE));
602 // Translate the index-based result of MPI_Waitsome into a
603 // partitioning on the requests.
604 int current_offset = 0;
605 current = first;
606 for (int index = 0; index < num_completed; ++index) {
607 using std::iter_swap;
609 // Move "current" to the request object at this index
610 advance(current, indices[index] - current_offset);
611 current_offset = indices[index];
613 // Finish up the request and swap it into the "completed
614 // requests" partition.
615 current->m_requests[0] = requests[indices[index]];
616 --start_of_completed;
617 iter_swap(current, start_of_completed);
620 // We have satisfied some requests, so we are done.
621 return start_of_completed;
624 // There are some nontrivial requests, so we must continue our
625 // busy waiting loop.
626 n = 0;
627 current = first;
631 // We cannot ever get here
632 BOOST_ASSERT(false);
635 /**
636 * @brief Test whether some non-blocking requests have completed.
638 * This routine takes in a set of requests stored in the iterator
639 * range @c [first,last) and tests to see if any of the requests has
640 * completed. It completes all of the requests it can, partitioning
641 * the input sequence into pending requests followed by completed
642 * requests. If an output iterator is provided, @c status objects
643 * will be emitted for each of the completed requests. This routine
644 * is similar to @c wait_some, but does not wait until any requests
645 * have completed. This routine provides functionality equivalent to
646 * @c MPI_Testsome.
648 * @param first The iterator that denotes the beginning of the
649 * sequence of request objects.
651 * @param last The iterator that denotes the end of the sequence of
652 * request objects. This may not be equal to @c first.
654 * @param out If provided, the @c status objects corresponding to
655 * completed requests will be emitted through this output iterator.
657 * @returns If the @p out parameter was provided, a pair containing
658 * the output iterator @p out after all of the @c status objects have
659 * been written through it and an iterator referencing the first
660 * completed request. If no @p out parameter was provided, only the
661 * iterator referencing the first completed request will be emitted.
663 template<typename BidirectionalIterator, typename OutputIterator>
664 std::pair<OutputIterator, BidirectionalIterator>
665 test_some(BidirectionalIterator first, BidirectionalIterator last,
666 OutputIterator out)
668 BidirectionalIterator current = first;
669 BidirectionalIterator start_of_completed = last;
670 while (current != start_of_completed) {
671 // Check if we have found a completed request.
672 if (optional<status> result = current->test()) {
673 using std::iter_swap;
675 // Emit the resulting status object
676 *out++ = *result;
678 // We're expanding the set of completed requests
679 --start_of_completed;
681 // Swap the request we just completed with the last request that
682 // has not yet been tested.
683 iter_swap(current, start_of_completed);
685 continue;
688 // Move to the next request.
689 ++current;
692 // Finish up by fixing the order of the completed set to match the
693 // order in which we emitted status objects, then return.
694 std::reverse(start_of_completed, last);
695 return std::make_pair(out, start_of_completed);
699 * \overload
701 template<typename BidirectionalIterator>
702 BidirectionalIterator
703 test_some(BidirectionalIterator first, BidirectionalIterator last)
705 BidirectionalIterator current = first;
706 BidirectionalIterator start_of_completed = last;
707 while (current != start_of_completed) {
708 // Check if we have found a completed request.
709 if (optional<status> result = current->test()) {
710 using std::iter_swap;
712 // We're expanding the set of completed requests
713 --start_of_completed;
715 // Swap the request we just completed with the last request that
716 // has not yet been tested.
717 iter_swap(current, start_of_completed);
719 continue;
722 // Move to the next request.
723 ++current;
726 return start_of_completed;
729 } } // end namespace boost::mpi
732 #endif // BOOST_MPI_NONBLOCKING_HPP