diff --git a/include/boost/mpi/nonblocking.hpp b/include/boost/mpi/nonblocking.hpp index 6275b519..fd24e36f 100644 --- a/include/boost/mpi/nonblocking.hpp +++ b/include/boost/mpi/nonblocking.hpp @@ -41,7 +41,9 @@ namespace boost { namespace mpi { * * @returns A pair containing the status object that corresponds to * the completed operation and the iterator referencing the completed - * request. + * request. If there are no active handles (distance(first, last) == 0, + * all requests already done or null requests), returns an empty + * status and iterator "last". */ template std::pair @@ -56,6 +58,7 @@ wait_any(ForwardIterator first, ForwardIterator last) bool all_trivial_requests = true; difference_type n = 0; + bool has_outstanding_request = false; ForwardIterator current = first; while (true) { // Check if we have found a completed request. If so, return it. @@ -63,6 +66,8 @@ wait_any(ForwardIterator first, ForwardIterator last) optional result = current->test(); if (bool(result)) { return std::make_pair(*result, current); + } else { + has_outstanding_request = true; } } @@ -93,16 +98,18 @@ wait_any(ForwardIterator first, ForwardIterator last) BOOST_MPI_CHECK_RESULT(MPI_Waitany, (n, detail::c_data(requests), &index, &stat.m_status)); - // We don't have a notion of empty requests or status objects, - // so this is an error. + // No active handles if (index == MPI_UNDEFINED) - boost::throw_exception(exception("MPI_Waitany", MPI_ERR_REQUEST)); + return std::make_pair(stat, last); // Find the iterator corresponding to the completed request. current = first; advance(current, index); *current->trivial() = requests[index]; return std::make_pair(stat, current); + } else if (!has_outstanding_request) { + // No active handles + return std::make_pair(status{}, last); } // There are some nontrivial requests, so we must continue our @@ -110,6 +117,7 @@ wait_any(ForwardIterator first, ForwardIterator last) n = 0; current = first; all_trivial_requests = true; + has_outstanding_request = false; } } diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 634c61f1..e5896f17 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -38,6 +38,7 @@ test-suite mpi [ mpi-test ring_test : : : 2 3 4 7 8 13 17 ] [ mpi-test sendrecv_test : : : 1 4 7 48 ] [ mpi-test wait_any_test : : : 1 4 7 20 ] + [ mpi-test wait_any_on_null : : : 1 2 ] [ mpi-test wait_all_vector_test : : : 2 ] [ mpi-test wait_all_on_null : : : 1 2 ] [ mpi-test scan_test ] diff --git a/test/wait_any_on_null.cpp b/test/wait_any_on_null.cpp new file mode 100644 index 00000000..8bc81742 --- /dev/null +++ b/test/wait_any_on_null.cpp @@ -0,0 +1,64 @@ +// Copyright (C) 2021 Steffen Hirschmann + +// 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) + +#include +#include +#include + +#define BOOST_TEST_NO_MAIN +#define BOOST_TEST_ALTERNATIVE_INIT_API +#define BOOST_TEST_MODULE mpi_wait_on_null +#include + +/** Check default constructed requests. + * Must not deadlock. + */ +BOOST_AUTO_TEST_CASE(wait_any_default_constructed_request) +{ + std::vector req(1); + boost::mpi::status s; + decltype(req)::iterator it; + std::tie(s, it) = boost::mpi::wait_any(req.begin(), req.end()); + BOOST_CHECK(it == req.end()); +} + +/** Check a trivial request twice. + */ +BOOST_AUTO_TEST_CASE(wait_any_all_trivial_and_done) +{ + boost::mpi::communicator comm; + std::vector req; + int dummy1 = 1, dummy2 = 0; + + req.push_back(comm.irecv(comm.rank(), 0, dummy2)); + comm.isend(comm.rank(), 0, dummy1); + + boost::mpi::status s; + decltype(req)::iterator it; + // All trivial requests + std::tie(s, it) = boost::mpi::wait_any(req.begin(), req.end()); + BOOST_CHECK(it != req.end()); + BOOST_CHECK(it == req.begin()); + BOOST_CHECK(s.count()); + BOOST_CHECK(*s.count() == 1); + + // Call a second time + std::tie(s, it) = boost::mpi::wait_any(req.begin(), req.end()); + BOOST_CHECK(it == req.end()); + BOOST_CHECK(s.count()); + // empty status according to MPI 3.1 ยง3.7.3, l.39 (p. 52) + BOOST_CHECK(*s.count() == 0); + BOOST_CHECK(!s.cancelled()); + BOOST_CHECK(s.source() == MPI_ANY_SOURCE); + BOOST_CHECK(s.tag() == MPI_ANY_TAG); + BOOST_CHECK(s.error() == MPI_SUCCESS); +} + +int main(int argc, char **argv) +{ + boost::mpi::environment env(argc, argv); + return boost::unit_test::unit_test_main(init_unit_test, argc, argv); +}