nonblocking.hpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733
  1. // Copyright (C) 2006 Douglas Gregor <doug.gregor -at- gmail.com>.
  2. // Use, modification and distribution is subject to the Boost Software
  3. // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
  4. // http://www.boost.org/LICENSE_1_0.txt)
  5. /** @file nonblocking.hpp
  6. *
  7. * This header defines operations for completing non-blocking
  8. * communication requests.
  9. */
  10. #ifndef BOOST_MPI_NONBLOCKING_HPP
  11. #define BOOST_MPI_NONBLOCKING_HPP
  12. #include <boost/mpi/config.hpp>
  13. #include <vector>
  14. #include <iterator> // for std::iterator_traits
  15. #include <boost/optional.hpp>
  16. #include <utility> // for std::pair
  17. #include <algorithm> // for iter_swap, reverse
  18. #include <boost/static_assert.hpp>
  19. #include <boost/mpi/request.hpp>
  20. #include <boost/mpi/status.hpp>
  21. #include <boost/mpi/exception.hpp>
  22. namespace boost { namespace mpi {
  23. /**
  24. * @brief Wait until any non-blocking request has completed.
  25. *
  26. * This routine takes in a set of requests stored in the iterator
  27. * range @c [first,last) and waits until any of these requests has
  28. * been completed. It provides functionality equivalent to
  29. * @c MPI_Waitany.
  30. *
  31. * @param first The iterator that denotes the beginning of the
  32. * sequence of request objects.
  33. *
  34. * @param last The iterator that denotes the end of the sequence of
  35. * request objects. This may not be equal to @c first.
  36. *
  37. * @returns A pair containing the status object that corresponds to
  38. * the completed operation and the iterator referencing the completed
  39. * request.
  40. */
  41. template<typename ForwardIterator>
  42. std::pair<status, ForwardIterator>
  43. wait_any(ForwardIterator first, ForwardIterator last)
  44. {
  45. using std::advance;
  46. BOOST_ASSERT(first != last);
  47. typedef typename std::iterator_traits<ForwardIterator>::difference_type
  48. difference_type;
  49. bool all_trivial_requests = true;
  50. difference_type n = 0;
  51. ForwardIterator current = first;
  52. while (true) {
  53. // Check if we have found a completed request. If so, return it.
  54. if (current->active()) {
  55. optional<status> result = current->test();
  56. if (bool(result)) {
  57. return std::make_pair(*result, current);
  58. }
  59. }
  60. // Check if this request (and all others before it) are "trivial"
  61. // requests, e.g., they can be represented with a single
  62. // MPI_Request.
  63. // We could probably ignore non trivial request that are inactive,
  64. // but we can assume that a mix of trivial and non trivial requests
  65. // is unlikely enough not to care.
  66. all_trivial_requests = all_trivial_requests && current->trivial();
  67. // Move to the next request.
  68. ++n;
  69. if (++current == last) {
  70. // We have reached the end of the list. If all requests thus far
  71. // have been trivial, we can call MPI_Waitany directly, because
  72. // it may be more efficient than our busy-wait semantics.
  73. if (all_trivial_requests) {
  74. std::vector<MPI_Request> requests;
  75. requests.reserve(n);
  76. for (current = first; current != last; ++current) {
  77. requests.push_back(*current->trivial());
  78. }
  79. // Let MPI wait until one of these operations completes.
  80. int index;
  81. status stat;
  82. BOOST_MPI_CHECK_RESULT(MPI_Waitany,
  83. (n, detail::c_data(requests), &index, &stat.m_status));
  84. // We don't have a notion of empty requests or status objects,
  85. // so this is an error.
  86. if (index == MPI_UNDEFINED)
  87. boost::throw_exception(exception("MPI_Waitany", MPI_ERR_REQUEST));
  88. // Find the iterator corresponding to the completed request.
  89. current = first;
  90. advance(current, index);
  91. *current->trivial() = requests[index];
  92. return std::make_pair(stat, current);
  93. }
  94. // There are some nontrivial requests, so we must continue our
  95. // busy waiting loop.
  96. n = 0;
  97. current = first;
  98. all_trivial_requests = true;
  99. }
  100. }
  101. // We cannot ever get here
  102. BOOST_ASSERT(false);
  103. }
  104. /**
  105. * @brief Test whether any non-blocking request has completed.
  106. *
  107. * This routine takes in a set of requests stored in the iterator
  108. * range @c [first,last) and tests whether any of these requests has
  109. * been completed. This routine is similar to @c wait_any, but will
  110. * not block waiting for requests to completed. It provides
  111. * functionality equivalent to @c MPI_Testany.
  112. *
  113. * @param first The iterator that denotes the beginning of the
  114. * sequence of request objects.
  115. *
  116. * @param last The iterator that denotes the end of the sequence of
  117. * request objects.
  118. *
  119. * @returns If any outstanding requests have completed, a pair
  120. * containing the status object that corresponds to the completed
  121. * operation and the iterator referencing the completed
  122. * request. Otherwise, an empty @c optional<>.
  123. */
  124. template<typename ForwardIterator>
  125. optional<std::pair<status, ForwardIterator> >
  126. test_any(ForwardIterator first, ForwardIterator last)
  127. {
  128. while (first != last) {
  129. // Check if we have found a completed request. If so, return it.
  130. if (optional<status> result = first->test()) {
  131. return std::make_pair(*result, first);
  132. }
  133. ++first;
  134. }
  135. // We found nothing
  136. return optional<std::pair<status, ForwardIterator> >();
  137. }
  138. /**
  139. * @brief Wait until all non-blocking requests have completed.
  140. *
  141. * This routine takes in a set of requests stored in the iterator
  142. * range @c [first,last) and waits until all of these requests have
  143. * been completed. It provides functionality equivalent to
  144. * @c MPI_Waitall.
  145. *
  146. * @param first The iterator that denotes the beginning of the
  147. * sequence of request objects.
  148. *
  149. * @param last The iterator that denotes the end of the sequence of
  150. * request objects.
  151. *
  152. * @param out If provided, an output iterator through which the
  153. * status of each request will be emitted. The @c status objects are
  154. * emitted in the same order as the requests are retrieved from
  155. * @c [first,last).
  156. *
  157. * @returns If an @p out parameter was provided, the value @c out
  158. * after all of the @c status objects have been emitted.
  159. */
  160. template<typename ForwardIterator, typename OutputIterator>
  161. OutputIterator
  162. wait_all(ForwardIterator first, ForwardIterator last, OutputIterator out)
  163. {
  164. typedef typename std::iterator_traits<ForwardIterator>::difference_type
  165. difference_type;
  166. using std::distance;
  167. difference_type num_outstanding_requests = distance(first, last);
  168. std::vector<status> results(num_outstanding_requests);
  169. std::vector<bool> completed(num_outstanding_requests);
  170. while (num_outstanding_requests > 0) {
  171. bool all_trivial_requests = true;
  172. difference_type idx = 0;
  173. for (ForwardIterator current = first; current != last; ++current, ++idx) {
  174. if (!completed[idx]) {
  175. if (!current->active()) {
  176. completed[idx] = true;
  177. --num_outstanding_requests;
  178. } else if (optional<status> stat = current->test()) {
  179. // This outstanding request has been completed. We're done.
  180. results[idx] = *stat;
  181. completed[idx] = true;
  182. --num_outstanding_requests;
  183. all_trivial_requests = false;
  184. } else {
  185. // Check if this request (and all others before it) are "trivial"
  186. // requests, e.g., they can be represented with a single
  187. // MPI_Request.
  188. all_trivial_requests = all_trivial_requests && current->trivial();
  189. }
  190. }
  191. }
  192. // If we have yet to fulfill any requests and all of the requests
  193. // are trivial (i.e., require only a single MPI_Request to be
  194. // fulfilled), call MPI_Waitall directly.
  195. if (all_trivial_requests
  196. && num_outstanding_requests == (difference_type)results.size()) {
  197. std::vector<MPI_Request> requests;
  198. requests.reserve(num_outstanding_requests);
  199. for (ForwardIterator current = first; current != last; ++current)
  200. requests.push_back(*current->trivial());
  201. // Let MPI wait until all of these operations completes.
  202. std::vector<MPI_Status> stats(num_outstanding_requests);
  203. BOOST_MPI_CHECK_RESULT(MPI_Waitall,
  204. (num_outstanding_requests, detail::c_data(requests),
  205. detail::c_data(stats)));
  206. for (std::vector<MPI_Status>::iterator i = stats.begin();
  207. i != stats.end(); ++i, ++out) {
  208. status stat;
  209. stat.m_status = *i;
  210. *out = stat;
  211. }
  212. return out;
  213. }
  214. all_trivial_requests = false;
  215. }
  216. return std::copy(results.begin(), results.end(), out);
  217. }
  218. /**
  219. * \overload
  220. */
  221. template<typename ForwardIterator>
  222. void
  223. wait_all(ForwardIterator first, ForwardIterator last)
  224. {
  225. typedef typename std::iterator_traits<ForwardIterator>::difference_type
  226. difference_type;
  227. using std::distance;
  228. difference_type num_outstanding_requests = distance(first, last);
  229. std::vector<bool> completed(num_outstanding_requests, false);
  230. while (num_outstanding_requests > 0) {
  231. bool all_trivial_requests = true;
  232. difference_type idx = 0;
  233. for (ForwardIterator current = first; current != last; ++current, ++idx) {
  234. if (!completed[idx]) {
  235. if (!current->active()) {
  236. completed[idx] = true;
  237. --num_outstanding_requests;
  238. } else if (optional<status> stat = current->test()) {
  239. // This outstanding request has been completed.
  240. completed[idx] = true;
  241. --num_outstanding_requests;
  242. all_trivial_requests = false;
  243. } else {
  244. // Check if this request (and all others before it) are "trivial"
  245. // requests, e.g., they can be represented with a single
  246. // MPI_Request.
  247. all_trivial_requests = all_trivial_requests && current->trivial();
  248. }
  249. }
  250. }
  251. // If we have yet to fulfill any requests and all of the requests
  252. // are trivial (i.e., require only a single MPI_Request to be
  253. // fulfilled), call MPI_Waitall directly.
  254. if (all_trivial_requests
  255. && num_outstanding_requests == (difference_type)completed.size()) {
  256. std::vector<MPI_Request> requests;
  257. requests.reserve(num_outstanding_requests);
  258. for (ForwardIterator current = first; current != last; ++current)
  259. requests.push_back(*current->trivial());
  260. // Let MPI wait until all of these operations completes.
  261. BOOST_MPI_CHECK_RESULT(MPI_Waitall,
  262. (num_outstanding_requests, detail::c_data(requests),
  263. MPI_STATUSES_IGNORE));
  264. // Signal completion
  265. num_outstanding_requests = 0;
  266. }
  267. }
  268. }
  269. /**
  270. * @brief Tests whether all non-blocking requests have completed.
  271. *
  272. * This routine takes in a set of requests stored in the iterator
  273. * range @c [first,last) and determines whether all of these requests
  274. * have been completed. However, due to limitations of the underlying
  275. * MPI implementation, if any of the requests refers to a
  276. * non-blocking send or receive of a serialized data type, @c
  277. * test_all will always return the equivalent of @c false (i.e., the
  278. * requests cannot all be finished at this time). This routine
  279. * performs the same functionality as @c wait_all, except that this
  280. * routine will not block. This routine provides functionality
  281. * equivalent to @c MPI_Testall.
  282. *
  283. * @param first The iterator that denotes the beginning of the
  284. * sequence of request objects.
  285. *
  286. * @param last The iterator that denotes the end of the sequence of
  287. * request objects.
  288. *
  289. * @param out If provided and all requests hav been completed, an
  290. * output iterator through which the status of each request will be
  291. * emitted. The @c status objects are emitted in the same order as
  292. * the requests are retrieved from @c [first,last).
  293. *
  294. * @returns If an @p out parameter was provided, the value @c out
  295. * after all of the @c status objects have been emitted (if all
  296. * requests were completed) or an empty @c optional<>. If no @p out
  297. * parameter was provided, returns @c true if all requests have
  298. * completed or @c false otherwise.
  299. */
  300. template<typename ForwardIterator, typename OutputIterator>
  301. optional<OutputIterator>
  302. test_all(ForwardIterator first, ForwardIterator last, OutputIterator out)
  303. {
  304. std::vector<MPI_Request> requests;
  305. for (; first != last; ++first) {
  306. // If we have a non-trivial request, then no requests can be
  307. // completed.
  308. if (!first->trivial()) {
  309. return optional<OutputIterator>();
  310. }
  311. requests.push_back(*first->trivial());
  312. }
  313. int flag = 0;
  314. int n = requests.size();
  315. std::vector<MPI_Status> stats(n);
  316. BOOST_MPI_CHECK_RESULT(MPI_Testall, (n, detail::c_data(requests), &flag, detail::c_data(stats)));
  317. if (flag) {
  318. for (int i = 0; i < n; ++i, ++out) {
  319. status stat;
  320. stat.m_status = stats[i];
  321. *out = stat;
  322. }
  323. return out;
  324. } else {
  325. return optional<OutputIterator>();
  326. }
  327. }
  328. /**
  329. * \overload
  330. */
  331. template<typename ForwardIterator>
  332. bool
  333. test_all(ForwardIterator first, ForwardIterator last)
  334. {
  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->trivial()) {
  340. return false;
  341. }
  342. requests.push_back(*first->trivial());
  343. }
  344. int flag = 0;
  345. int n = requests.size();
  346. BOOST_MPI_CHECK_RESULT(MPI_Testall,
  347. (n, detail::c_data(requests), &flag, MPI_STATUSES_IGNORE));
  348. return flag != 0;
  349. }
  350. /**
  351. * @brief Wait until some non-blocking requests have completed.
  352. *
  353. * This routine takes in a set of requests stored in the iterator
  354. * range @c [first,last) and waits until at least one of the requests
  355. * has completed. It then completes all of the requests it can,
  356. * partitioning the input sequence into pending requests followed by
  357. * completed requests. If an output iterator is provided, @c status
  358. * objects will be emitted for each of the completed requests. This
  359. * routine provides functionality equivalent to @c MPI_Waitsome.
  360. *
  361. * @param first The iterator that denotes the beginning of the
  362. * sequence of request objects.
  363. *
  364. * @param last The iterator that denotes the end of the sequence of
  365. * request objects. This may not be equal to @c first.
  366. *
  367. * @param out If provided, the @c status objects corresponding to
  368. * completed requests will be emitted through this output iterator.
  369. * @returns If the @p out parameter was provided, a pair containing
  370. * the output iterator @p out after all of the @c status objects have
  371. * been written through it and an iterator referencing the first
  372. * completed request. If no @p out parameter was provided, only the
  373. * iterator referencing the first completed request will be emitted.
  374. */
  375. template<typename BidirectionalIterator, typename OutputIterator>
  376. std::pair<OutputIterator, BidirectionalIterator>
  377. wait_some(BidirectionalIterator first, BidirectionalIterator last,
  378. OutputIterator out)
  379. {
  380. using std::advance;
  381. if (first == last)
  382. return std::make_pair(out, first);
  383. typedef typename std::iterator_traits<BidirectionalIterator>::difference_type
  384. difference_type;
  385. bool all_trivial_requests = true;
  386. difference_type n = 0;
  387. BidirectionalIterator current = first;
  388. BidirectionalIterator start_of_completed = last;
  389. while (true) {
  390. // Check if we have found a completed request.
  391. if (optional<status> result = current->test()) {
  392. using std::iter_swap;
  393. // Emit the resulting status object
  394. *out++ = *result;
  395. // We're expanding the set of completed requests
  396. --start_of_completed;
  397. if (current == start_of_completed) {
  398. // If we have hit the end of the list of pending
  399. // requests. Finish up by fixing the order of the completed
  400. // set to match the order in which we emitted status objects,
  401. // then return.
  402. std::reverse(start_of_completed, last);
  403. return std::make_pair(out, start_of_completed);
  404. }
  405. // Swap the request we just completed with the last request that
  406. // has not yet been tested.
  407. iter_swap(current, start_of_completed);
  408. continue;
  409. }
  410. // Check if this request (and all others before it) are "trivial"
  411. // requests, e.g., they can be represented with a single
  412. // MPI_Request.
  413. all_trivial_requests = all_trivial_requests && current->trivial();
  414. // Move to the next request.
  415. ++n;
  416. if (++current == start_of_completed) {
  417. if (start_of_completed != last) {
  418. // We have satisfied some requests. Make the order of the
  419. // completed requests match that of the status objects we've
  420. // already emitted and we're done.
  421. std::reverse(start_of_completed, last);
  422. return std::make_pair(out, start_of_completed);
  423. }
  424. // We have reached the end of the list. If all requests thus far
  425. // have been trivial, we can call MPI_Waitsome directly, because
  426. // it may be more efficient than our busy-wait semantics.
  427. if (all_trivial_requests) {
  428. std::vector<MPI_Request> requests;
  429. std::vector<int> indices(n);
  430. std::vector<MPI_Status> stats(n);
  431. requests.reserve(n);
  432. for (current = first; current != last; ++current)
  433. requests.push_back(*current->trivial());
  434. // Let MPI wait until some of these operations complete.
  435. int num_completed;
  436. BOOST_MPI_CHECK_RESULT(MPI_Waitsome,
  437. (n, detail::c_data(requests), &num_completed, detail::c_data(indices),
  438. detail::c_data(stats)));
  439. // Translate the index-based result of MPI_Waitsome into a
  440. // partitioning on the requests.
  441. int current_offset = 0;
  442. current = first;
  443. for (int index = 0; index < num_completed; ++index, ++out) {
  444. using std::iter_swap;
  445. // Move "current" to the request object at this index
  446. advance(current, indices[index] - current_offset);
  447. current_offset = indices[index];
  448. // Emit the status object
  449. status stat;
  450. stat.m_status = stats[index];
  451. *out = stat;
  452. // Finish up the request and swap it into the "completed
  453. // requests" partition.
  454. *current->trivial() = requests[indices[index]];
  455. --start_of_completed;
  456. iter_swap(current, start_of_completed);
  457. }
  458. // We have satisfied some requests. Make the order of the
  459. // completed requests match that of the status objects we've
  460. // already emitted and we're done.
  461. std::reverse(start_of_completed, last);
  462. return std::make_pair(out, start_of_completed);
  463. }
  464. // There are some nontrivial requests, so we must continue our
  465. // busy waiting loop.
  466. n = 0;
  467. current = first;
  468. }
  469. }
  470. // We cannot ever get here
  471. BOOST_ASSERT(false);
  472. }
  473. /**
  474. * \overload
  475. */
  476. template<typename BidirectionalIterator>
  477. BidirectionalIterator
  478. wait_some(BidirectionalIterator first, BidirectionalIterator last)
  479. {
  480. using std::advance;
  481. if (first == last)
  482. return first;
  483. typedef typename std::iterator_traits<BidirectionalIterator>::difference_type
  484. difference_type;
  485. bool all_trivial_requests = true;
  486. difference_type n = 0;
  487. BidirectionalIterator current = first;
  488. BidirectionalIterator start_of_completed = last;
  489. while (true) {
  490. // Check if we have found a completed request.
  491. if (optional<status> result = current->test()) {
  492. using std::iter_swap;
  493. // We're expanding the set of completed requests
  494. --start_of_completed;
  495. // If we have hit the end of the list of pending requests, we're
  496. // done.
  497. if (current == start_of_completed)
  498. return start_of_completed;
  499. // Swap the request we just completed with the last request that
  500. // has not yet been tested.
  501. iter_swap(current, start_of_completed);
  502. continue;
  503. }
  504. // Check if this request (and all others before it) are "trivial"
  505. // requests, e.g., they can be represented with a single
  506. // MPI_Request.
  507. all_trivial_requests = all_trivial_requests && current->trivial();
  508. // Move to the next request.
  509. ++n;
  510. if (++current == start_of_completed) {
  511. // If we have satisfied some requests, we're done.
  512. if (start_of_completed != last)
  513. return start_of_completed;
  514. // We have reached the end of the list. If all requests thus far
  515. // have been trivial, we can call MPI_Waitsome directly, because
  516. // it may be more efficient than our busy-wait semantics.
  517. if (all_trivial_requests) {
  518. std::vector<MPI_Request> requests;
  519. std::vector<int> indices(n);
  520. requests.reserve(n);
  521. for (current = first; current != last; ++current)
  522. requests.push_back(*current->trivial());
  523. // Let MPI wait until some of these operations complete.
  524. int num_completed;
  525. BOOST_MPI_CHECK_RESULT(MPI_Waitsome,
  526. (n, detail::c_data(requests), &num_completed, detail::c_data(indices),
  527. MPI_STATUSES_IGNORE));
  528. // Translate the index-based result of MPI_Waitsome into a
  529. // partitioning on the requests.
  530. int current_offset = 0;
  531. current = first;
  532. for (int index = 0; index < num_completed; ++index) {
  533. using std::iter_swap;
  534. // Move "current" to the request object at this index
  535. advance(current, indices[index] - current_offset);
  536. current_offset = indices[index];
  537. // Finish up the request and swap it into the "completed
  538. // requests" partition.
  539. *current->trivial() = requests[indices[index]];
  540. --start_of_completed;
  541. iter_swap(current, start_of_completed);
  542. }
  543. // We have satisfied some requests, so we are done.
  544. return start_of_completed;
  545. }
  546. // There are some nontrivial requests, so we must continue our
  547. // busy waiting loop.
  548. n = 0;
  549. current = first;
  550. }
  551. }
  552. // We cannot ever get here
  553. BOOST_ASSERT(false);
  554. }
  555. /**
  556. * @brief Test whether some non-blocking requests have completed.
  557. *
  558. * This routine takes in a set of requests stored in the iterator
  559. * range @c [first,last) and tests to see if any of the requests has
  560. * completed. It completes all of the requests it can, partitioning
  561. * the input sequence into pending requests followed by completed
  562. * requests. If an output iterator is provided, @c status objects
  563. * will be emitted for each of the completed requests. This routine
  564. * is similar to @c wait_some, but does not wait until any requests
  565. * have completed. This routine provides functionality equivalent to
  566. * @c MPI_Testsome.
  567. *
  568. * @param first The iterator that denotes the beginning of the
  569. * sequence of request objects.
  570. *
  571. * @param last The iterator that denotes the end of the sequence of
  572. * request objects. This may not be equal to @c first.
  573. *
  574. * @param out If provided, the @c status objects corresponding to
  575. * completed requests will be emitted through this output iterator.
  576. * @returns If the @p out parameter was provided, a pair containing
  577. * the output iterator @p out after all of the @c status objects have
  578. * been written through it and an iterator referencing the first
  579. * completed request. If no @p out parameter was provided, only the
  580. * iterator referencing the first completed request will be emitted.
  581. */
  582. template<typename BidirectionalIterator, typename OutputIterator>
  583. std::pair<OutputIterator, BidirectionalIterator>
  584. test_some(BidirectionalIterator first, BidirectionalIterator last,
  585. OutputIterator out)
  586. {
  587. BidirectionalIterator current = first;
  588. BidirectionalIterator start_of_completed = last;
  589. while (current != start_of_completed) {
  590. // Check if we have found a completed request.
  591. if (optional<status> result = current->test()) {
  592. using std::iter_swap;
  593. // Emit the resulting status object
  594. *out++ = *result;
  595. // We're expanding the set of completed requests
  596. --start_of_completed;
  597. // Swap the request we just completed with the last request that
  598. // has not yet been tested.
  599. iter_swap(current, start_of_completed);
  600. continue;
  601. }
  602. // Move to the next request.
  603. ++current;
  604. }
  605. // Finish up by fixing the order of the completed set to match the
  606. // order in which we emitted status objects, then return.
  607. std::reverse(start_of_completed, last);
  608. return std::make_pair(out, start_of_completed);
  609. }
  610. /**
  611. * \overload
  612. */
  613. template<typename BidirectionalIterator>
  614. BidirectionalIterator
  615. test_some(BidirectionalIterator first, BidirectionalIterator last)
  616. {
  617. BidirectionalIterator current = first;
  618. BidirectionalIterator start_of_completed = last;
  619. while (current != start_of_completed) {
  620. // Check if we have found a completed request.
  621. if (optional<status> result = current->test()) {
  622. using std::iter_swap;
  623. // We're expanding the set of completed requests
  624. --start_of_completed;
  625. // Swap the request we just completed with the last request that
  626. // has not yet been tested.
  627. iter_swap(current, start_of_completed);
  628. continue;
  629. }
  630. // Move to the next request.
  631. ++current;
  632. }
  633. return start_of_completed;
  634. }
  635. } } // end namespace boost::mpi
  636. #endif // BOOST_MPI_NONBLOCKING_HPP