739 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			739 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
| // 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 (current->m_requests[0] != MPI_REQUEST_NULL &&
 | |
|         (current->m_requests[1] != MPI_REQUEST_NULL ||
 | |
|          current->m_handler)) {
 | |
|       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)
 | |
| {
 | |
|   while (first != last) {
 | |
|     // Check if we have found a completed request. If so, return it.
 | |
|     if (optional<status> result = first->test()) {
 | |
|       return std::make_pair(*result, first);
 | |
|     }
 | |
|     ++first;
 | |
|   }
 | |
| 
 | |
|   // 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
 | 
