Index: boost/mpi/exception.hpp =================================================================== --- boost/mpi/exception.hpp (revision 81596) +++ boost/mpi/exception.hpp (working copy) @@ -35,14 +35,9 @@ /** * Build a new @c exception exception. * - * @param routine The MPI routine in which the error - * occurred. This should be a pointer to a string constant: it - * will not be copied. - * - * @param result_code The result code returned from the MPI - * routine that aborted with an error. + * @param message A descriptive message explaining the error. */ - exception(const char* routine, int result_code); + exception(const std::string& msg); virtual ~exception() throw(); @@ -54,9 +49,46 @@ return this->message.c_str(); } + /** + * @brief Retrieve the result code returned from the MPI routine + * that reported the error, if any. Sub classes should redefine this + * as necessary. + */ + virtual int result_code() const { return MPI_SUCCESS; } + + protected: + /// The formatted error message + std::string message; +}; + +/** @brief Catch-all exception class for MPI errors. + * + * Instances of this class will be thrown when an MPI error + * occurs. MPI failures that trigger these exceptions may or may not + * be recoverable, depending on the underlying MPI + * implementation. Consult the documentation for your MPI + * implementation to determine the effect of MPI errors. + */ +class BOOST_MPI_DECL api_exception : public exception +{ + public: + /** + * Build a new @c exception exception. + * + * @param routine The MPI routine in which the error + * occurred. This should be a pointer to a string constant: it + * will not be copied. + * + * @param result_code The result code returned from the MPI + * routine that aborted with an error. + */ + api_exception(const char* routine, int result_code); + + virtual ~api_exception() throw(); + /** Retrieve the name of the MPI routine that reported the error. */ const char* routine() const { return routine_; } - + /** * @brief Retrieve the result code returned from the MPI routine * that reported the error. @@ -80,9 +112,6 @@ /// The failed result code reported by the MPI implementation. int result_code_; - - /// The formatted error message - std::string message; }; /** @@ -95,8 +124,8 @@ { \ int _check_result = MPIFunc Args; \ if (_check_result != MPI_SUCCESS) \ - boost::throw_exception(boost::mpi::exception(#MPIFunc, \ - _check_result)); \ + boost::throw_exception(boost::mpi::api_exception(#MPIFunc, \ + _check_result)); \ } } } // end namespace boost::mpi Index: boost/mpi/nonblocking.hpp =================================================================== --- boost/mpi/nonblocking.hpp (revision 81596) +++ boost/mpi/nonblocking.hpp (working copy) @@ -91,7 +91,7 @@ // 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)); + boost::throw_exception(api_exception("MPI_Waitany", MPI_ERR_REQUEST)); // Find the iterator corresponding to the completed request. current = first; Index: boost/mpi/environment.hpp =================================================================== --- boost/mpi/environment.hpp (revision 81596) +++ boost/mpi/environment.hpp (working copy) @@ -17,9 +17,47 @@ #include #include #include +#include namespace boost { namespace mpi { +namespace threading { +/** @brief specify the supported threading level. + * + * Based on MPI 2 standard/8.7.3 + */ +enum level { + /** Only one thread will execute. + */ + single = MPI_THREAD_SINGLE, + /** Only main thread will do MPI calls. + * + * The process may be multi-threaded, but only the main + * thread will make MPI calls (all MPI calls are ``funneled'' + * to the main thread). + */ + funneled = MPI_THREAD_FUNNELED, + /** Only one thread at the time do MPI calls. + * + * The process may be multi-threaded, and multiple + * threads may make MPI calls, but only one at a time: + * MPI calls are not made concurrently from two distinct + * threads (all MPI calls are ``serialized''). + */ + serialized = MPI_THREAD_SERIALIZED, + /** Multiple thread may do MPI calls. + * + * Multiple threads may call MPI, with no restrictions. + */ + multiple = MPI_THREAD_MULTIPLE +}; +/** Formated output for threading level. */ +std::ostream& operator<<(std::ostream& out, level l); + +/** Formated output for threading level. */ +std::istream& operator>>(std::istream& in, level l); + +} // namespace threading /** @brief Initialize, finalize, and query the MPI environment. * * The @c environment class is used to initialize, finalize, and @@ -62,6 +100,29 @@ * program if it is destructed due to an uncaught exception. */ explicit environment(bool abort_on_exception = true); + /** Initialize the MPI environment. + * + * If the MPI environment has not already been initialized, + * initializes MPI with a call to @c MPI_Init_thread. Since this + * constructor does not take command-line arguments (@c argc and @c + * argv), it is only available when the underlying MPI + * implementation supports calling @c MPI_Init with @c NULL + * arguments, indicated by the macro @c + * BOOST_MPI_HAS_NOARG_INITIALIZATION. + * + * If the required threading support level cannot be provided, + * construction will throw and mpi will be finalized. + * + * @param prefered the prefered level of threading support, will + * try to meet that request if possible. + * + * @param required the minimal level of threading support. + * + * @param abort_on_exception When true, this object will abort the + * program if it is destructed due to an uncaught exception. + */ + explicit environment(threading::level prefered, threading::level required, + bool abort_on_exception = true); #endif /** Initialize the MPI environment. @@ -80,6 +141,33 @@ */ environment(int& argc, char** &argv, bool abort_on_exception = true); + /** Initialize the MPI environment. + * + * If the MPI environment has not already been initialized, + * initializes MPI with a call to @c MPI_Init_thread. + * + * If the required threading support level cannot be provided, + * construction will throw and mpi will be finalized. + * + * @param argc The number of arguments provided in @p argv, as + * passed into the program's @c main function. + * + * @param argv The array of argument strings passed to the program + * via @c main. + * + * @param prefered the prefered level of threading support, will + * try to meet that request if possible. + * + * @param required the minimal level of threading support, this constructor + * will throw if the requirement is not fulfilled. + * + * @param abort_on_exception When true, this object will abort the + * program if it is destructed due to an uncaught exception. + */ + environment(int& argc, char** &argv, + threading::level prefered, threading::level required, + bool abort_on_exception = true); + /** Shuts down the MPI environment. * * If this @c environment object was used to initialize the MPI @@ -185,13 +273,26 @@ */ static std::string processor_name(); + /** Query the current level of thread support. + */ + static threading::level thread_level(); + + /** Are we in the main thread? + */ + static bool main_thread(); + private: + /// Code factorization on ctor. + void call_mpi_init(int* argc, char*** argv, + threading::level prefered, threading::level required); + +private: /// Whether this environment object called MPI_Init bool i_initialized; /// Whether we should abort if the destructor is bool abort_on_exception; - + /// The number of reserved tags. static const int num_reserved_tags = 1; }; Index: libs/mpi/test/mt_level_test.cpp =================================================================== --- libs/mpi/test/mt_level_test.cpp (revision 0) +++ libs/mpi/test/mt_level_test.cpp (revision 0) @@ -0,0 +1,26 @@ +// Copyright (C) 2005-2006 Douglas Gregor + +// 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) + +// Test a few basic feature for multi-threading. +// It is quite basic since the support is dependent +// on the underlying implementation those threading +// support is allowed to varry by the MPI standard. + +#include +#include + +using namespace boost::mpi; +using namespace std; + +int test_main(int argc, char* argv[]) +{ + using namespace boost::mpi::threading; + BOOST_CHECK((single < funneled)); + BOOST_CHECK((funneled < serialized)); + BOOST_CHECK((serialized < multiple)); + + return 0; +} Index: libs/mpi/test/mt_init_failed_test.cpp =================================================================== --- libs/mpi/test/mt_init_failed_test.cpp (revision 0) +++ libs/mpi/test/mt_init_failed_test.cpp (revision 0) @@ -0,0 +1,37 @@ +// Copyright (C) 2005-2006 Alain Miniussi + +// 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) + +// Test a few basic feature for multi-threading. +// It is quite basic since the support is dependent +// on the underlying implementation those threading +// support is allowed to varry by the MPI standard. + +#include +#include +#include + +namespace mpi = boost::mpi; +namespace th = boost::mpi::threading; +using namespace std; + +bool do_init(int argc, char* argv[]) +{ + bool success = false; + bool clean_failure = false; + try { + mpi::environment env(argc, argv, th::multiple, th::multiple); + success = env.thread_level() == th::multiple; + } catch (mpi::exception& e) { + clean_failure = true; + } + return success || clean_failure; +} + +int test_main(int argc, char* argv[]) +{ + BOOST_CHECK(do_init(argc,argv)); + return 0; +} Index: libs/mpi/test/Jamfile.v2 =================================================================== --- libs/mpi/test/Jamfile.v2 (revision 81596) +++ libs/mpi/test/Jamfile.v2 (working copy) @@ -23,6 +23,9 @@ [ mpi-test broadcast_test : : : 2 17 ] [ mpi-test gather_test ] [ mpi-test is_mpi_op_test : : : 1 ] + [ mpi-test mt_level_test : : : 1 4 ] + [ mpi-test mt_init_test : : : 1 4 ] + [ mpi-test mt_init_failed_test : : : 1 4 ] # Note: Microsoft MPI fails nonblocking_test on 1 processor [ mpi-test nonblocking_test ] [ mpi-test reduce_test ] Index: libs/mpi/test/mt_init_test.cpp =================================================================== --- libs/mpi/test/mt_init_test.cpp (revision 0) +++ libs/mpi/test/mt_init_test.cpp (revision 0) @@ -0,0 +1,26 @@ +// Copyright (C) 2005-2006 Alain Miniussi + +// 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) + +// Test a few basic feature for multi-threading. +// It is quite basic since the support is dependent +// on the underlying implementation those threading +// support is allowed to varry by the MPI standard. + +#include +#include + +using namespace boost::mpi; +using namespace std; + +int test_main(int argc, char* argv[]) +{ + // just test for one value, I could not find the doc in mpi.jam + // to pass params through argc/argv + namespace th = threading; + boost::mpi::environment env(argc, argv, th::multiple, th::single); + BOOST_CHECK((env.thread_level() >= th::single)); + return 0; +} Index: libs/mpi/src/request.cpp =================================================================== --- libs/mpi/src/request.cpp (revision 81596) +++ libs/mpi/src/request.cpp (working copy) @@ -42,13 +42,13 @@ // one when throwing the exception. if (stats[0].MPI_ERROR == MPI_SUCCESS || stats[0].MPI_ERROR == MPI_ERR_PENDING) - boost::throw_exception(exception("MPI_Waitall", stats[1].MPI_ERROR)); + boost::throw_exception(api_exception("MPI_Waitall", stats[1].MPI_ERROR)); else - boost::throw_exception(exception("MPI_Waitall", stats[0].MPI_ERROR)); + boost::throw_exception(api_exception("MPI_Waitall", stats[0].MPI_ERROR)); } else if (error_code != MPI_SUCCESS) { // There was an error somewhere in the MPI_Waitall call; throw // an exception for it. - boost::throw_exception(exception("MPI_Waitall", error_code)); + boost::throw_exception(api_exception("MPI_Waitall", error_code)); } // No errors. Returns the first status structure. @@ -85,13 +85,13 @@ // one when throwing the exception. if (stats[0].MPI_ERROR == MPI_SUCCESS || stats[0].MPI_ERROR == MPI_ERR_PENDING) - boost::throw_exception(exception("MPI_Testall", stats[1].MPI_ERROR)); + boost::throw_exception(api_exception("MPI_Testall", stats[1].MPI_ERROR)); else - boost::throw_exception(exception("MPI_Testall", stats[0].MPI_ERROR)); + boost::throw_exception(api_exception("MPI_Testall", stats[0].MPI_ERROR)); } else if (error_code != MPI_SUCCESS) { // There was an error somewhere in the MPI_Testall call; throw // an exception for it. - boost::throw_exception(exception("MPI_Testall", error_code)); + boost::throw_exception(api_exception("MPI_Testall", error_code)); } // No errors. Returns the second status structure if the send has Index: libs/mpi/src/exception.cpp =================================================================== --- libs/mpi/src/exception.cpp (revision 81596) +++ libs/mpi/src/exception.cpp (working copy) @@ -10,20 +10,27 @@ namespace boost { namespace mpi { -exception::exception(const char* routine, int result_code) - : routine_(routine), result_code_(result_code) +exception::exception(const std::string& msg) + : message(msg) { +} + +exception::~exception() throw() { } + + +api_exception::api_exception(const char* routine, int result_code) + : exception(routine), routine_(routine), result_code_(result_code) +{ // Query the MPI implementation for its reason for failure char buffer[MPI_MAX_ERROR_STRING]; int len; MPI_Error_string(result_code, buffer, &len); // Construct the complete error message - message.append(routine_); message.append(": "); message.append(buffer, len); } -exception::~exception() throw() { } +api_exception::~api_exception() throw() { } } } // end namespace boost::mpi Index: libs/mpi/src/environment.cpp =================================================================== --- libs/mpi/src/environment.cpp (revision 81596) +++ libs/mpi/src/environment.cpp (working copy) @@ -8,23 +8,102 @@ #include #include #include +#include #include #include #include +#include +#include namespace boost { namespace mpi { +namespace threading { +std::ostream& +operator<<(std::ostream& out, level l) +{ + switch(l) { + case single: + out << "single"; + break; + case funneled: + out << "funneled"; + break; + case serialized: + out << "serialized"; + break; + case multiple: + out << "multiple"; + break; + default: + out << "[" << int(l) << ']'; + break; + } + return out; +} +std::istream& +operator>>(std::istream& in, level l) +{ + std::string buf; + in >> buf; + if (in.good()) { + boost::to_upper(buf); + if (buf == "SINGLE") { + l = single; + } else if (buf == "FUNNELED") { + l = funneled; + } else if (buf == "SERIALIZED") { + l = serialized; + } else if (buf == "MULTIPLE") { + l = multiple; + } else { + in.setstate(std::ios_base::failbit); + } + } + return in; +} +} // namespace threading + +void +environment::call_mpi_init(int* argc, char*** argv, + threading::level prefered, threading::level required) +{ + BOOST_ASSERT(required <= prefered); + if (!initialized()) { + int provided; + BOOST_MPI_CHECK_RESULT(MPI_Init_thread, + (argc, argv, int(prefered), &provided )); + if (threading::level(provided) < required) { + // no cleanup necessary in dtor + i_initialized = false; + // we need to finalize right here so that the + // the aplication does not leave without finalization. + BOOST_MPI_CHECK_RESULT(MPI_Finalize, ()); + std::ostringstream fmt; + fmt << "Asked for prefered threading support level '" + << prefered << "' with minimum required '" + << required << "', got '" << threading::level(provided) << "'\n"; + boost::throw_exception(boost::mpi::exception(fmt.str())); + } else { + i_initialized = true; + } + } + MPI_Errhandler_set(MPI_COMM_WORLD, MPI_ERRORS_RETURN); +} + #ifdef BOOST_MPI_HAS_NOARG_INITIALIZATION environment::environment(bool abort_on_exception) : i_initialized(false), abort_on_exception(abort_on_exception) { - if (!initialized()) { - BOOST_MPI_CHECK_RESULT(MPI_Init, (0, 0)); - i_initialized = true; - } + call_mpi_init(0, 0, threading::single, threading::single); +} - MPI_Errhandler_set(MPI_COMM_WORLD, MPI_ERRORS_RETURN); +environment::environment(threading::level prefered, threading::level required, + bool abort_on_exception) + : i_initialized(false), + abort_on_exception(abort_on_exception) +{ + call_mpi_init(0, 0, prefered, required); } #endif @@ -32,12 +111,16 @@ : i_initialized(false), abort_on_exception(abort_on_exception) { - if (!initialized()) { - BOOST_MPI_CHECK_RESULT(MPI_Init, (&argc, &argv)); - i_initialized = true; - } + call_mpi_init(&argc, &argv, threading::single, threading::single); +} - MPI_Errhandler_set(MPI_COMM_WORLD, MPI_ERRORS_RETURN); +environment::environment(int& argc, char** &argv, + threading::level prefered, threading::level required, + bool abort_on_exception) + : i_initialized(false), + abort_on_exception(abort_on_exception) +{ + call_mpi_init(&argc, &argv, prefered, required); } environment::~environment() @@ -122,4 +205,20 @@ return std::string(name, len); } +threading::level environment::thread_level() +{ + int level; + + BOOST_MPI_CHECK_RESULT(MPI_Query_thread, (&level)); + return static_cast(level); +} + +bool environment::main_thread() +{ + int isit; + + BOOST_MPI_CHECK_RESULT(MPI_Is_thread_main, (&isit)); + return bool(isit); +} + } } // end namespace boost::mpi