1 // (C) Copyright 2007
2 // Douglas Gregor <doug.gregor -at- gmail.com>
3 // Andreas Kloeckner <inform -at- tiker.net>
4 
5 // Use, modification and distribution is subject to the Boost Software
6 // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
7 // http://www.boost.org/LICENSE_1_0.txt)
8 
9 //  Authors: Douglas Gregor, Andreas Kloeckner
10 
11 /** @file py_nonblocking.cpp
12  *
13  *  This file reflects the Boost.MPI nonblocking operations into Python
14  *  functions.
15  */
16 
17 #include <vector>
18 #include <iterator>
19 #include <algorithm>
20 #include <boost/operators.hpp>
21 #include <boost/python.hpp>
22 #include <boost/python/stl_iterator.hpp>
23 #include <boost/python/suite/indexing/vector_indexing_suite.hpp>
24 #include <boost/mpi.hpp>
25 #include "request_with_value.hpp"
26 
27 using namespace std;
28 using namespace boost::python;
29 using namespace boost::mpi;
30 
31 
32 
33 
34 namespace
35 {
36   template <class ValueType, class RequestIterator>
37   class py_call_output_iterator :
38     public boost::output_iterator_helper<
39       py_call_output_iterator<ValueType, RequestIterator> >
40   {
41     private:
42       object m_callable;
43       RequestIterator m_request_iterator;
44 
45     public:
py_call_output_iterator(object callable,const RequestIterator & req_it)46       explicit py_call_output_iterator(object callable,
47           const RequestIterator &req_it)
48         : m_callable(callable), m_request_iterator(req_it)
49       { }
50 
operator =(ValueType const & v)51       py_call_output_iterator &operator=(ValueType const &v)
52       {
53         m_callable((m_request_iterator++)->get_value_or_none(), v);
54         return *this;
55       }
56   };
57 
58 
59 
60 
61   typedef std::vector<python::request_with_value> request_list;
62   typedef py_call_output_iterator<status, request_list::iterator>
63     status_value_iterator;
64 
65 
66 
67 
make_request_list_from_py_list(object iterable)68   std::shared_ptr<request_list> make_request_list_from_py_list(object iterable)
69   {
70     std::shared_ptr<request_list> result(new request_list);
71     std::copy(
72         stl_input_iterator<python::request_with_value>(iterable),
73         stl_input_iterator<python::request_with_value>(),
74         back_inserter(*result));
75     return result;
76   }
77 
78 
79 
80 
81   class request_list_indexing_suite :
82     public vector_indexing_suite<request_list, false, request_list_indexing_suite>
83   {
84     public:
85       // FIXME: requests are not comparable, thus __contains__ makes no sense.
86       // Unfortunately, indexing_suites insist on having __contains__ available.
87       // Just make it error out for now.
88 
89       static bool
contains(request_list & container,request const & key)90         contains(request_list& container, request const& key)
91         {
92           PyErr_SetString(PyExc_NotImplementedError, "mpi requests are not comparable");
93           throw error_already_set();
94         }
95   };
96 
97 
98 
99 
check_request_list_not_empty(const request_list & requests)100   void check_request_list_not_empty(const request_list &requests)
101   {
102     if (requests.size() == 0)
103     {
104       PyErr_SetString(PyExc_ValueError, "cannot wait on an empty request vector");
105       throw error_already_set();
106     }
107 
108   }
109 
110 
111 
112 
113 
wrap_wait_any(request_list & requests)114   object wrap_wait_any(request_list &requests)
115   {
116     check_request_list_not_empty(requests);
117 
118     pair<status, request_list::iterator> result =
119       wait_any(requests.begin(), requests.end());
120 
121     return boost::python::make_tuple(
122         result.second->get_value_or_none(),
123         result.first,
124         distance(requests.begin(), result.second));
125   }
126 
127 
128 
129 
wrap_test_any(request_list & requests)130   object wrap_test_any(request_list &requests)
131   {
132     check_request_list_not_empty(requests);
133     ::boost::optional<pair<status, request_list::iterator> > result =
134       test_any(requests.begin(), requests.end());
135 
136     if (result)
137       return boost::python::make_tuple(
138           result->second->get_value_or_none(),
139           result->first,
140           distance(requests.begin(), result->second));
141     else
142       return object();
143   }
144 
145 
146 
147 
148 
wrap_wait_all(request_list & requests,object py_callable)149   void wrap_wait_all(request_list &requests, object py_callable)
150   {
151     check_request_list_not_empty(requests);
152     if (py_callable != object())
153       wait_all(requests.begin(), requests.end(),
154           status_value_iterator(py_callable, requests.begin()));
155     else
156       wait_all(requests.begin(), requests.end());
157   }
158 
159 
160 
161 
wrap_test_all(request_list & requests,object py_callable)162   bool wrap_test_all(request_list &requests, object py_callable)
163   {
164     check_request_list_not_empty(requests);
165     if (py_callable != object())
166       return bool(test_all(requests.begin(), requests.end(),
167 			   status_value_iterator(py_callable, requests.begin())));
168     else
169       return bool(test_all(requests.begin(), requests.end()));
170   }
171 
172 
173 
174 
wrap_wait_some(request_list & requests,object py_callable)175   int wrap_wait_some(request_list &requests, object py_callable)
176   {
177     check_request_list_not_empty(requests);
178     request_list::iterator first_completed;
179     if (py_callable != object())
180       first_completed = wait_some(requests.begin(), requests.end(),
181           status_value_iterator(py_callable, requests.begin())).second;
182     else
183       first_completed = wait_some(requests.begin(), requests.end());
184 
185     return distance(requests.begin(), first_completed);
186   }
187 
188 
189 
190 
wrap_test_some(request_list & requests,object py_callable)191   int wrap_test_some(request_list &requests, object py_callable)
192   {
193     check_request_list_not_empty(requests);
194     request_list::iterator first_completed;
195     if (py_callable != object())
196       first_completed = test_some(requests.begin(), requests.end(),
197           status_value_iterator(py_callable, requests.begin())).second;
198     else
199       first_completed = test_some(requests.begin(), requests.end());
200 
201     return distance(requests.begin(), first_completed);
202   }
203 }
204 
205 
206 
207 
208 namespace boost { namespace mpi { namespace python {
209 
210 extern const char* request_list_init_docstring;
211 extern const char* request_list_append_docstring;
212 
213 extern const char* nonblocking_wait_any_docstring;
214 extern const char* nonblocking_test_any_docstring;
215 extern const char* nonblocking_wait_all_docstring;
216 extern const char* nonblocking_test_all_docstring;
217 extern const char* nonblocking_wait_some_docstring;
218 extern const char* nonblocking_test_some_docstring;
219 
export_nonblocking()220 void export_nonblocking()
221 {
222   using boost::python::arg;
223 
224   {
225     typedef request_list cl;
226     class_<cl>("RequestList", "A list of Request objects.")
227       .def("__init__", make_constructor(make_request_list_from_py_list),
228           /*arg("iterable"),*/ request_list_init_docstring)
229       .def(request_list_indexing_suite())
230       ;
231   }
232 
233   def("wait_any", wrap_wait_any,
234       (arg("requests")),
235       nonblocking_wait_any_docstring);
236   def("test_any", wrap_test_any,
237       (arg("requests")),
238       nonblocking_test_any_docstring);
239 
240   def("wait_all", wrap_wait_all,
241       (arg("requests"), arg("callable") = object()),
242       nonblocking_wait_all_docstring);
243   def("test_all", wrap_test_all,
244       (arg("requests"), arg("callable") = object()),
245       nonblocking_test_all_docstring);
246 
247   def("wait_some", wrap_wait_some,
248       (arg("requests"), arg("callable") = object()),
249       nonblocking_wait_some_docstring);
250   def("test_some", wrap_test_some,
251       (arg("requests"), arg("callable") = object()),
252       nonblocking_test_some_docstring);
253 }
254 
255 } } }
256