blob: 22f289f9e84c03adbdf89a42f3fb85c31a94293d [file] [log] [blame]
// (C) Copyright 2007
// Douglas Gregor <doug.gregor -at- gmail.com>
// Andreas Kloeckner <inform -at- tiker.net>
// 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)
// Authors: Douglas Gregor, Andreas Kloeckner
/** @file py_nonblocking.cpp
*
* This file reflects the Boost.MPI nonblocking operations into Python
* functions.
*/
#include <vector>
#include <iterator>
#include <algorithm>
#include <boost/operators.hpp>
#include <boost/python.hpp>
#include <boost/python/stl_iterator.hpp>
#include <boost/python/suite/indexing/vector_indexing_suite.hpp>
#include <boost/mpi.hpp>
#include "request_with_value.hpp"
using namespace std;
using namespace boost::python;
using namespace boost::mpi;
namespace
{
template <class ValueType, class RequestIterator>
class py_call_output_iterator :
public boost::output_iterator_helper<
py_call_output_iterator<ValueType, RequestIterator> >
{
private:
object m_callable;
RequestIterator m_request_iterator;
public:
explicit py_call_output_iterator(object callable,
const RequestIterator &req_it)
: m_callable(callable), m_request_iterator(req_it)
{ }
py_call_output_iterator &operator=(ValueType const &v)
{
m_callable((m_request_iterator++)->get_value_or_none(), v);
return *this;
}
};
typedef std::vector<python::request_with_value> request_list;
typedef py_call_output_iterator<status, request_list::iterator>
status_value_iterator;
std::auto_ptr<request_list> make_request_list_from_py_list(object iterable)
{
std::auto_ptr<request_list> result(new request_list);
std::copy(
stl_input_iterator<python::request_with_value>(iterable),
stl_input_iterator<python::request_with_value>(),
back_inserter(*result));
return result;
}
class request_list_indexing_suite :
public vector_indexing_suite<request_list, false, request_list_indexing_suite>
{
public:
// FIXME: requests are not comparable, thus __contains__ makes no sense.
// Unfortunately, indexing_suites insist on having __contains__ available.
// Just make it error out for now.
static bool
contains(request_list& container, request const& key)
{
PyErr_SetString(PyExc_NotImplementedError, "mpi requests are not comparable");
throw error_already_set();
}
};
void check_request_list_not_empty(const request_list &requests)
{
if (requests.size() == 0)
{
PyErr_SetString(PyExc_ValueError, "cannot wait on an empty request vector");
throw error_already_set();
}
}
object wrap_wait_any(request_list &requests)
{
check_request_list_not_empty(requests);
pair<status, request_list::iterator> result =
wait_any(requests.begin(), requests.end());
return make_tuple(
result.second->get_value_or_none(),
result.first,
distance(requests.begin(), result.second));
}
object wrap_test_any(request_list &requests)
{
check_request_list_not_empty(requests);
::boost::optional<pair<status, request_list::iterator> > result =
test_any(requests.begin(), requests.end());
if (result)
return make_tuple(
result->second->get_value_or_none(),
result->first,
distance(requests.begin(), result->second));
else
return object();
}
void wrap_wait_all(request_list &requests, object py_callable)
{
check_request_list_not_empty(requests);
if (py_callable != object())
wait_all(requests.begin(), requests.end(),
status_value_iterator(py_callable, requests.begin()));
else
wait_all(requests.begin(), requests.end());
}
bool wrap_test_all(request_list &requests, object py_callable)
{
check_request_list_not_empty(requests);
if (py_callable != object())
return test_all(requests.begin(), requests.end(),
status_value_iterator(py_callable, requests.begin()));
else
return test_all(requests.begin(), requests.end());
}
int wrap_wait_some(request_list &requests, object py_callable)
{
check_request_list_not_empty(requests);
request_list::iterator first_completed;
if (py_callable != object())
first_completed = wait_some(requests.begin(), requests.end(),
status_value_iterator(py_callable, requests.begin())).second;
else
first_completed = wait_some(requests.begin(), requests.end());
return distance(requests.begin(), first_completed);
}
int wrap_test_some(request_list &requests, object py_callable)
{
check_request_list_not_empty(requests);
request_list::iterator first_completed;
if (py_callable != object())
first_completed = test_some(requests.begin(), requests.end(),
status_value_iterator(py_callable, requests.begin())).second;
else
first_completed = test_some(requests.begin(), requests.end());
return distance(requests.begin(), first_completed);
}
}
namespace boost { namespace mpi { namespace python {
extern const char* request_list_init_docstring;
extern const char* request_list_append_docstring;
extern const char* nonblocking_wait_any_docstring;
extern const char* nonblocking_test_any_docstring;
extern const char* nonblocking_wait_all_docstring;
extern const char* nonblocking_test_all_docstring;
extern const char* nonblocking_wait_some_docstring;
extern const char* nonblocking_test_some_docstring;
void export_nonblocking()
{
using boost::python::arg;
{
typedef request_list cl;
class_<cl>("RequestList", "A list of Request objects.")
.def("__init__", make_constructor(make_request_list_from_py_list),
/*arg("iterable"),*/ request_list_init_docstring)
.def(request_list_indexing_suite())
;
}
def("wait_any", wrap_wait_any,
(arg("requests")),
nonblocking_wait_any_docstring);
def("test_any", wrap_test_any,
(arg("requests")),
nonblocking_test_any_docstring);
def("wait_all", wrap_wait_all,
(arg("requests"), arg("callable") = object()),
nonblocking_wait_all_docstring);
def("test_all", wrap_test_all,
(arg("requests"), arg("callable") = object()),
nonblocking_test_all_docstring);
def("wait_some", wrap_wait_some,
(arg("requests"), arg("callable") = object()),
nonblocking_wait_some_docstring);
def("test_some", wrap_test_some,
(arg("requests"), arg("callable") = object()),
nonblocking_test_some_docstring);
}
} } }