You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and dots ('.'), can be up to 35 characters long. Letters must be lowercase.
732 lines
25 KiB
732 lines
25 KiB
// Copyright (C) 2006 Douglas Gregor <doug.gregor -at- gmail.com>. |
|
|
|
// Use, modification and distribution is subject to the Boost Software |
|
// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at |
|
// http://www.boost.org/LICENSE_1_0.txt) |
|
|
|
/** @file nonblocking.hpp |
|
* |
|
* This header defines operations for completing non-blocking |
|
* communication requests. |
|
*/ |
|
#ifndef BOOST_MPI_NONBLOCKING_HPP |
|
#define BOOST_MPI_NONBLOCKING_HPP |
|
|
|
#include <boost/mpi/config.hpp> |
|
#include <vector> |
|
#include <iterator> // for std::iterator_traits |
|
#include <boost/optional.hpp> |
|
#include <utility> // for std::pair |
|
#include <algorithm> // for iter_swap, reverse |
|
#include <boost/static_assert.hpp> |
|
#include <boost/mpi/request.hpp> |
|
#include <boost/mpi/status.hpp> |
|
#include <boost/mpi/exception.hpp> |
|
|
|
namespace boost { namespace mpi { |
|
|
|
/** |
|
* @brief Wait until any non-blocking request has completed. |
|
* |
|
* This routine takes in a set of requests stored in the iterator |
|
* range @c [first,last) and waits until any of these requests has |
|
* been completed. It provides functionality equivalent to |
|
* @c MPI_Waitany. |
|
* |
|
* @param first The iterator that denotes the beginning of the |
|
* sequence of request objects. |
|
* |
|
* @param last The iterator that denotes the end of the sequence of |
|
* request objects. This may not be equal to @c first. |
|
* |
|
* @returns A pair containing the status object that corresponds to |
|
* the completed operation and the iterator referencing the completed |
|
* request. |
|
*/ |
|
template<typename ForwardIterator> |
|
std::pair<status, ForwardIterator> |
|
wait_any(ForwardIterator first, ForwardIterator last) |
|
{ |
|
using std::advance; |
|
|
|
BOOST_ASSERT(first != last); |
|
|
|
typedef typename std::iterator_traits<ForwardIterator>::difference_type |
|
difference_type; |
|
|
|
bool all_trivial_requests = true; |
|
difference_type n = 0; |
|
ForwardIterator current = first; |
|
while (true) { |
|
// Check if we have found a completed request. If so, return it. |
|
if (optional<status> result = current->test()) |
|
return std::make_pair(*result, current); |
|
|
|
// Check if this request (and all others before it) are "trivial" |
|
// requests, e.g., they can be represented with a single |
|
// MPI_Request. |
|
all_trivial_requests = |
|
all_trivial_requests |
|
&& !current->m_handler |
|
&& current->m_requests[1] == MPI_REQUEST_NULL; |
|
|
|
// Move to the next request. |
|
++n; |
|
if (++current == last) { |
|
// We have reached the end of the list. If all requests thus far |
|
// have been trivial, we can call MPI_Waitany directly, because |
|
// it may be more efficient than our busy-wait semantics. |
|
if (all_trivial_requests) { |
|
std::vector<MPI_Request> requests; |
|
requests.reserve(n); |
|
for (current = first; current != last; ++current) |
|
requests.push_back(current->m_requests[0]); |
|
|
|
// Let MPI wait until one of these operations completes. |
|
int index; |
|
status stat; |
|
BOOST_MPI_CHECK_RESULT(MPI_Waitany, |
|
(n, &requests[0], &index, &stat.m_status)); |
|
|
|
// We don't have a notion of empty requests or status objects, |
|
// so this is an error. |
|
if (index == MPI_UNDEFINED) |
|
boost::throw_exception(exception("MPI_Waitany", MPI_ERR_REQUEST)); |
|
|
|
// Find the iterator corresponding to the completed request. |
|
current = first; |
|
advance(current, index); |
|
current->m_requests[0] = requests[index]; |
|
return std::make_pair(stat, current); |
|
} |
|
|
|
// There are some nontrivial requests, so we must continue our |
|
// busy waiting loop. |
|
n = 0; |
|
current = first; |
|
all_trivial_requests = true; |
|
} |
|
} |
|
|
|
// We cannot ever get here |
|
BOOST_ASSERT(false); |
|
} |
|
|
|
/** |
|
* @brief Test whether any non-blocking request has completed. |
|
* |
|
* This routine takes in a set of requests stored in the iterator |
|
* range @c [first,last) and tests whether any of these requests has |
|
* been completed. This routine is similar to @c wait_any, but will |
|
* not block waiting for requests to completed. It provides |
|
* functionality equivalent to @c MPI_Testany. |
|
* |
|
* @param first The iterator that denotes the beginning of the |
|
* sequence of request objects. |
|
* |
|
* @param last The iterator that denotes the end of the sequence of |
|
* request objects. |
|
* |
|
* @returns If any outstanding requests have completed, a pair |
|
* containing the status object that corresponds to the completed |
|
* operation and the iterator referencing the completed |
|
* request. Otherwise, an empty @c optional<>. |
|
*/ |
|
template<typename ForwardIterator> |
|
optional<std::pair<status, ForwardIterator> > |
|
test_any(ForwardIterator first, ForwardIterator last) |
|
{ |
|
for (ForwardIterator current = first; first != last; ++first) { |
|
// Check if we have found a completed request. If so, return it. |
|
if (optional<status> result = current->test()) |
|
return std::make_pair(*result, current); |
|
} |
|
|
|
// We found nothing |
|
return optional<std::pair<status, ForwardIterator> >(); |
|
} |
|
|
|
/** |
|
* @brief Wait until all non-blocking requests have completed. |
|
* |
|
* This routine takes in a set of requests stored in the iterator |
|
* range @c [first,last) and waits until all of these requests have |
|
* been completed. It provides functionality equivalent to |
|
* @c MPI_Waitall. |
|
* |
|
* @param first The iterator that denotes the beginning of the |
|
* sequence of request objects. |
|
* |
|
* @param last The iterator that denotes the end of the sequence of |
|
* request objects. |
|
* |
|
* @param out If provided, an output iterator through which the |
|
* status of each request will be emitted. The @c status objects are |
|
* emitted in the same order as the requests are retrieved from |
|
* @c [first,last). |
|
* |
|
* @returns If an @p out parameter was provided, the value @c out |
|
* after all of the @c status objects have been emitted. |
|
*/ |
|
template<typename ForwardIterator, typename OutputIterator> |
|
OutputIterator |
|
wait_all(ForwardIterator first, ForwardIterator last, OutputIterator out) |
|
{ |
|
typedef typename std::iterator_traits<ForwardIterator>::difference_type |
|
difference_type; |
|
|
|
using std::distance; |
|
|
|
difference_type num_outstanding_requests = distance(first, last); |
|
|
|
std::vector<status> results(num_outstanding_requests); |
|
std::vector<bool> completed(num_outstanding_requests); |
|
|
|
while (num_outstanding_requests > 0) { |
|
bool all_trivial_requests = true; |
|
difference_type idx = 0; |
|
for (ForwardIterator current = first; current != last; ++current, ++idx) { |
|
if (!completed[idx]) { |
|
if (optional<status> stat = current->test()) { |
|
// This outstanding request has been completed. We're done. |
|
results[idx] = *stat; |
|
completed[idx] = true; |
|
--num_outstanding_requests; |
|
all_trivial_requests = false; |
|
} else { |
|
// Check if this request (and all others before it) are "trivial" |
|
// requests, e.g., they can be represented with a single |
|
// MPI_Request. |
|
all_trivial_requests = |
|
all_trivial_requests |
|
&& !current->m_handler |
|
&& current->m_requests[1] == MPI_REQUEST_NULL; |
|
} |
|
} |
|
} |
|
|
|
// If we have yet to fulfill any requests and all of the requests |
|
// are trivial (i.e., require only a single MPI_Request to be |
|
// fulfilled), call MPI_Waitall directly. |
|
if (all_trivial_requests |
|
&& num_outstanding_requests == (difference_type)results.size()) { |
|
std::vector<MPI_Request> requests; |
|
requests.reserve(num_outstanding_requests); |
|
for (ForwardIterator current = first; current != last; ++current) |
|
requests.push_back(current->m_requests[0]); |
|
|
|
// Let MPI wait until all of these operations completes. |
|
std::vector<MPI_Status> stats(num_outstanding_requests); |
|
BOOST_MPI_CHECK_RESULT(MPI_Waitall, |
|
(num_outstanding_requests, &requests[0], |
|
&stats[0])); |
|
|
|
for (std::vector<MPI_Status>::iterator i = stats.begin(); |
|
i != stats.end(); ++i, ++out) { |
|
status stat; |
|
stat.m_status = *i; |
|
*out = stat; |
|
} |
|
|
|
return out; |
|
} |
|
|
|
all_trivial_requests = false; |
|
} |
|
|
|
return std::copy(results.begin(), results.end(), out); |
|
} |
|
|
|
/** |
|
* \overload |
|
*/ |
|
template<typename ForwardIterator> |
|
void |
|
wait_all(ForwardIterator first, ForwardIterator last) |
|
{ |
|
typedef typename std::iterator_traits<ForwardIterator>::difference_type |
|
difference_type; |
|
|
|
using std::distance; |
|
|
|
difference_type num_outstanding_requests = distance(first, last); |
|
|
|
std::vector<bool> completed(num_outstanding_requests); |
|
|
|
while (num_outstanding_requests > 0) { |
|
bool all_trivial_requests = true; |
|
|
|
difference_type idx = 0; |
|
for (ForwardIterator current = first; current != last; ++current, ++idx) { |
|
if (!completed[idx]) { |
|
if (optional<status> stat = current->test()) { |
|
// This outstanding request has been completed. |
|
completed[idx] = true; |
|
--num_outstanding_requests; |
|
all_trivial_requests = false; |
|
} else { |
|
// Check if this request (and all others before it) are "trivial" |
|
// requests, e.g., they can be represented with a single |
|
// MPI_Request. |
|
all_trivial_requests = |
|
all_trivial_requests |
|
&& !current->m_handler |
|
&& current->m_requests[1] == MPI_REQUEST_NULL; |
|
} |
|
} |
|
} |
|
|
|
// If we have yet to fulfill any requests and all of the requests |
|
// are trivial (i.e., require only a single MPI_Request to be |
|
// fulfilled), call MPI_Waitall directly. |
|
if (all_trivial_requests |
|
&& num_outstanding_requests == (difference_type)completed.size()) { |
|
std::vector<MPI_Request> requests; |
|
requests.reserve(num_outstanding_requests); |
|
for (ForwardIterator current = first; current != last; ++current) |
|
requests.push_back(current->m_requests[0]); |
|
|
|
// Let MPI wait until all of these operations completes. |
|
BOOST_MPI_CHECK_RESULT(MPI_Waitall, |
|
(num_outstanding_requests, &requests[0], |
|
MPI_STATUSES_IGNORE)); |
|
|
|
// Signal completion |
|
num_outstanding_requests = 0; |
|
} |
|
} |
|
} |
|
|
|
/** |
|
* @brief Tests whether all non-blocking requests have completed. |
|
* |
|
* This routine takes in a set of requests stored in the iterator |
|
* range @c [first,last) and determines whether all of these requests |
|
* have been completed. However, due to limitations of the underlying |
|
* MPI implementation, if any of the requests refers to a |
|
* non-blocking send or receive of a serialized data type, @c |
|
* test_all will always return the equivalent of @c false (i.e., the |
|
* requests cannot all be finished at this time). This routine |
|
* performs the same functionality as @c wait_all, except that this |
|
* routine will not block. This routine provides functionality |
|
* equivalent to @c MPI_Testall. |
|
* |
|
* @param first The iterator that denotes the beginning of the |
|
* sequence of request objects. |
|
* |
|
* @param last The iterator that denotes the end of the sequence of |
|
* request objects. |
|
* |
|
* @param out If provided and all requests hav been completed, an |
|
* output iterator through which the status of each request will be |
|
* emitted. The @c status objects are emitted in the same order as |
|
* the requests are retrieved from @c [first,last). |
|
* |
|
* @returns If an @p out parameter was provided, the value @c out |
|
* after all of the @c status objects have been emitted (if all |
|
* requests were completed) or an empty @c optional<>. If no @p out |
|
* parameter was provided, returns @c true if all requests have |
|
* completed or @c false otherwise. |
|
*/ |
|
template<typename ForwardIterator, typename OutputIterator> |
|
optional<OutputIterator> |
|
test_all(ForwardIterator first, ForwardIterator last, OutputIterator out) |
|
{ |
|
std::vector<MPI_Request> requests; |
|
for (; first != last; ++first) { |
|
// If we have a non-trivial request, then no requests can be |
|
// completed. |
|
if (first->m_handler || first->m_requests[1] != MPI_REQUEST_NULL) |
|
return optional<OutputIterator>(); |
|
|
|
requests.push_back(first->m_requests[0]); |
|
} |
|
|
|
int flag = 0; |
|
int n = requests.size(); |
|
std::vector<MPI_Status> stats(n); |
|
BOOST_MPI_CHECK_RESULT(MPI_Testall, (n, &requests[0], &flag, &stats[0])); |
|
if (flag) { |
|
for (int i = 0; i < n; ++i, ++out) { |
|
status stat; |
|
stat.m_status = stats[i]; |
|
*out = stat; |
|
} |
|
return out; |
|
} else { |
|
return optional<OutputIterator>(); |
|
} |
|
} |
|
|
|
/** |
|
* \overload |
|
*/ |
|
template<typename ForwardIterator> |
|
bool |
|
test_all(ForwardIterator first, ForwardIterator last) |
|
{ |
|
std::vector<MPI_Request> requests; |
|
for (; first != last; ++first) { |
|
// If we have a non-trivial request, then no requests can be |
|
// completed. |
|
if (first->m_handler || first->m_requests[1] != MPI_REQUEST_NULL) |
|
return false; |
|
|
|
requests.push_back(first->m_requests[0]); |
|
} |
|
|
|
int flag = 0; |
|
int n = requests.size(); |
|
BOOST_MPI_CHECK_RESULT(MPI_Testall, |
|
(n, &requests[0], &flag, MPI_STATUSES_IGNORE)); |
|
return flag != 0; |
|
} |
|
|
|
/** |
|
* @brief Wait until some non-blocking requests have completed. |
|
* |
|
* This routine takes in a set of requests stored in the iterator |
|
* range @c [first,last) and waits until at least one of the requests |
|
* has completed. It then completes all of the requests it can, |
|
* partitioning the input sequence into pending requests followed by |
|
* completed requests. If an output iterator is provided, @c status |
|
* objects will be emitted for each of the completed requests. This |
|
* routine provides functionality equivalent to @c MPI_Waitsome. |
|
* |
|
* @param first The iterator that denotes the beginning of the |
|
* sequence of request objects. |
|
* |
|
* @param last The iterator that denotes the end of the sequence of |
|
* request objects. This may not be equal to @c first. |
|
* |
|
* @param out If provided, the @c status objects corresponding to |
|
* completed requests will be emitted through this output iterator. |
|
|
|
* @returns If the @p out parameter was provided, a pair containing |
|
* the output iterator @p out after all of the @c status objects have |
|
* been written through it and an iterator referencing the first |
|
* completed request. If no @p out parameter was provided, only the |
|
* iterator referencing the first completed request will be emitted. |
|
*/ |
|
template<typename BidirectionalIterator, typename OutputIterator> |
|
std::pair<OutputIterator, BidirectionalIterator> |
|
wait_some(BidirectionalIterator first, BidirectionalIterator last, |
|
OutputIterator out) |
|
{ |
|
using std::advance; |
|
|
|
if (first == last) |
|
return std::make_pair(out, first); |
|
|
|
typedef typename std::iterator_traits<BidirectionalIterator>::difference_type |
|
difference_type; |
|
|
|
bool all_trivial_requests = true; |
|
difference_type n = 0; |
|
BidirectionalIterator current = first; |
|
BidirectionalIterator start_of_completed = last; |
|
while (true) { |
|
// Check if we have found a completed request. |
|
if (optional<status> result = current->test()) { |
|
using std::iter_swap; |
|
|
|
// Emit the resulting status object |
|
*out++ = *result; |
|
|
|
// We're expanding the set of completed requests |
|
--start_of_completed; |
|
|
|
if (current == start_of_completed) { |
|
// If we have hit the end of the list of pending |
|
// requests. Finish up by fixing the order of the completed |
|
// set to match the order in which we emitted status objects, |
|
// then return. |
|
std::reverse(start_of_completed, last); |
|
return std::make_pair(out, start_of_completed); |
|
} |
|
|
|
// Swap the request we just completed with the last request that |
|
// has not yet been tested. |
|
iter_swap(current, start_of_completed); |
|
|
|
continue; |
|
} |
|
|
|
// Check if this request (and all others before it) are "trivial" |
|
// requests, e.g., they can be represented with a single |
|
// MPI_Request. |
|
all_trivial_requests = |
|
all_trivial_requests |
|
&& !current->m_handler |
|
&& current->m_requests[1] == MPI_REQUEST_NULL; |
|
|
|
// Move to the next request. |
|
++n; |
|
if (++current == start_of_completed) { |
|
if (start_of_completed != last) { |
|
// We have satisfied some requests. Make the order of the |
|
// completed requests match that of the status objects we've |
|
// already emitted and we're done. |
|
std::reverse(start_of_completed, last); |
|
return std::make_pair(out, start_of_completed); |
|
} |
|
|
|
// We have reached the end of the list. If all requests thus far |
|
// have been trivial, we can call MPI_Waitsome directly, because |
|
// it may be more efficient than our busy-wait semantics. |
|
if (all_trivial_requests) { |
|
std::vector<MPI_Request> requests; |
|
std::vector<int> indices(n); |
|
std::vector<MPI_Status> stats(n); |
|
requests.reserve(n); |
|
for (current = first; current != last; ++current) |
|
requests.push_back(current->m_requests[0]); |
|
|
|
// Let MPI wait until some of these operations complete. |
|
int num_completed; |
|
BOOST_MPI_CHECK_RESULT(MPI_Waitsome, |
|
(n, &requests[0], &num_completed, &indices[0], |
|
&stats[0])); |
|
|
|
// Translate the index-based result of MPI_Waitsome into a |
|
// partitioning on the requests. |
|
int current_offset = 0; |
|
current = first; |
|
for (int index = 0; index < num_completed; ++index, ++out) { |
|
using std::iter_swap; |
|
|
|
// Move "current" to the request object at this index |
|
advance(current, indices[index] - current_offset); |
|
current_offset = indices[index]; |
|
|
|
// Emit the status object |
|
status stat; |
|
stat.m_status = stats[index]; |
|
*out = stat; |
|
|
|
// Finish up the request and swap it into the "completed |
|
// requests" partition. |
|
current->m_requests[0] = requests[indices[index]]; |
|
--start_of_completed; |
|
iter_swap(current, start_of_completed); |
|
} |
|
|
|
// We have satisfied some requests. Make the order of the |
|
// completed requests match that of the status objects we've |
|
// already emitted and we're done. |
|
std::reverse(start_of_completed, last); |
|
return std::make_pair(out, start_of_completed); |
|
} |
|
|
|
// There are some nontrivial requests, so we must continue our |
|
// busy waiting loop. |
|
n = 0; |
|
current = first; |
|
} |
|
} |
|
|
|
// We cannot ever get here |
|
BOOST_ASSERT(false); |
|
} |
|
|
|
/** |
|
* \overload |
|
*/ |
|
template<typename BidirectionalIterator> |
|
BidirectionalIterator |
|
wait_some(BidirectionalIterator first, BidirectionalIterator last) |
|
{ |
|
using std::advance; |
|
|
|
if (first == last) |
|
return first; |
|
|
|
typedef typename std::iterator_traits<BidirectionalIterator>::difference_type |
|
difference_type; |
|
|
|
bool all_trivial_requests = true; |
|
difference_type n = 0; |
|
BidirectionalIterator current = first; |
|
BidirectionalIterator start_of_completed = last; |
|
while (true) { |
|
// Check if we have found a completed request. |
|
if (optional<status> result = current->test()) { |
|
using std::iter_swap; |
|
|
|
// We're expanding the set of completed requests |
|
--start_of_completed; |
|
|
|
// If we have hit the end of the list of pending requests, we're |
|
// done. |
|
if (current == start_of_completed) |
|
return start_of_completed; |
|
|
|
// Swap the request we just completed with the last request that |
|
// has not yet been tested. |
|
iter_swap(current, start_of_completed); |
|
|
|
continue; |
|
} |
|
|
|
// Check if this request (and all others before it) are "trivial" |
|
// requests, e.g., they can be represented with a single |
|
// MPI_Request. |
|
all_trivial_requests = |
|
all_trivial_requests |
|
&& !current->m_handler |
|
&& current->m_requests[1] == MPI_REQUEST_NULL; |
|
|
|
// Move to the next request. |
|
++n; |
|
if (++current == start_of_completed) { |
|
// If we have satisfied some requests, we're done. |
|
if (start_of_completed != last) |
|
return start_of_completed; |
|
|
|
// We have reached the end of the list. If all requests thus far |
|
// have been trivial, we can call MPI_Waitsome directly, because |
|
// it may be more efficient than our busy-wait semantics. |
|
if (all_trivial_requests) { |
|
std::vector<MPI_Request> requests; |
|
std::vector<int> indices(n); |
|
requests.reserve(n); |
|
for (current = first; current != last; ++current) |
|
requests.push_back(current->m_requests[0]); |
|
|
|
// Let MPI wait until some of these operations complete. |
|
int num_completed; |
|
BOOST_MPI_CHECK_RESULT(MPI_Waitsome, |
|
(n, &requests[0], &num_completed, &indices[0], |
|
MPI_STATUSES_IGNORE)); |
|
|
|
// Translate the index-based result of MPI_Waitsome into a |
|
// partitioning on the requests. |
|
int current_offset = 0; |
|
current = first; |
|
for (int index = 0; index < num_completed; ++index) { |
|
using std::iter_swap; |
|
|
|
// Move "current" to the request object at this index |
|
advance(current, indices[index] - current_offset); |
|
current_offset = indices[index]; |
|
|
|
// Finish up the request and swap it into the "completed |
|
// requests" partition. |
|
current->m_requests[0] = requests[indices[index]]; |
|
--start_of_completed; |
|
iter_swap(current, start_of_completed); |
|
} |
|
|
|
// We have satisfied some requests, so we are done. |
|
return start_of_completed; |
|
} |
|
|
|
// There are some nontrivial requests, so we must continue our |
|
// busy waiting loop. |
|
n = 0; |
|
current = first; |
|
} |
|
} |
|
|
|
// We cannot ever get here |
|
BOOST_ASSERT(false); |
|
} |
|
|
|
/** |
|
* @brief Test whether some non-blocking requests have completed. |
|
* |
|
* This routine takes in a set of requests stored in the iterator |
|
* range @c [first,last) and tests to see if any of the requests has |
|
* completed. It completes all of the requests it can, partitioning |
|
* the input sequence into pending requests followed by completed |
|
* requests. If an output iterator is provided, @c status objects |
|
* will be emitted for each of the completed requests. This routine |
|
* is similar to @c wait_some, but does not wait until any requests |
|
* have completed. This routine provides functionality equivalent to |
|
* @c MPI_Testsome. |
|
* |
|
* @param first The iterator that denotes the beginning of the |
|
* sequence of request objects. |
|
* |
|
* @param last The iterator that denotes the end of the sequence of |
|
* request objects. This may not be equal to @c first. |
|
* |
|
* @param out If provided, the @c status objects corresponding to |
|
* completed requests will be emitted through this output iterator. |
|
|
|
* @returns If the @p out parameter was provided, a pair containing |
|
* the output iterator @p out after all of the @c status objects have |
|
* been written through it and an iterator referencing the first |
|
* completed request. If no @p out parameter was provided, only the |
|
* iterator referencing the first completed request will be emitted. |
|
*/ |
|
template<typename BidirectionalIterator, typename OutputIterator> |
|
std::pair<OutputIterator, BidirectionalIterator> |
|
test_some(BidirectionalIterator first, BidirectionalIterator last, |
|
OutputIterator out) |
|
{ |
|
BidirectionalIterator current = first; |
|
BidirectionalIterator start_of_completed = last; |
|
while (current != start_of_completed) { |
|
// Check if we have found a completed request. |
|
if (optional<status> result = current->test()) { |
|
using std::iter_swap; |
|
|
|
// Emit the resulting status object |
|
*out++ = *result; |
|
|
|
// We're expanding the set of completed requests |
|
--start_of_completed; |
|
|
|
// Swap the request we just completed with the last request that |
|
// has not yet been tested. |
|
iter_swap(current, start_of_completed); |
|
|
|
continue; |
|
} |
|
|
|
// Move to the next request. |
|
++current; |
|
} |
|
|
|
// Finish up by fixing the order of the completed set to match the |
|
// order in which we emitted status objects, then return. |
|
std::reverse(start_of_completed, last); |
|
return std::make_pair(out, start_of_completed); |
|
} |
|
|
|
/** |
|
* \overload |
|
*/ |
|
template<typename BidirectionalIterator> |
|
BidirectionalIterator |
|
test_some(BidirectionalIterator first, BidirectionalIterator last) |
|
{ |
|
BidirectionalIterator current = first; |
|
BidirectionalIterator start_of_completed = last; |
|
while (current != start_of_completed) { |
|
// Check if we have found a completed request. |
|
if (optional<status> result = current->test()) { |
|
using std::iter_swap; |
|
|
|
// We're expanding the set of completed requests |
|
--start_of_completed; |
|
|
|
// Swap the request we just completed with the last request that |
|
// has not yet been tested. |
|
iter_swap(current, start_of_completed); |
|
|
|
continue; |
|
} |
|
|
|
// Move to the next request. |
|
++current; |
|
} |
|
|
|
return start_of_completed; |
|
} |
|
|
|
} } // end namespace boost::mpi |
|
|
|
|
|
#endif // BOOST_MPI_NONBLOCKING_HPP
|
|
|