1 // Copyright (C) 2018 Alain Miniussi <alain.miniussi@oca.eu>.
2 
3 // Use, modification and distribution is subject to the Boost Software
4 // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
5 // http://www.boost.org/LICENSE_1_0.txt)
6 
7 // Request implementation dtails
8 
9 // This header should be included only after the communicator and request
10 // classes has been defined.
11 #ifndef BOOST_MPI_REQUEST_HANDLERS_HPP
12 #define BOOST_MPI_REQUEST_HANDLERS_HPP
13 
14 #include <boost/mpi/skeleton_and_content_types.hpp>
15 
16 namespace boost { namespace mpi {
17 
18 namespace detail {
19 /**
20  * Internal data structure that stores everything required to manage
21  * the receipt of serialized data via a request object.
22  */
23 template<typename T>
24 struct serialized_irecv_data {
serialized_irecv_databoost::mpi::detail::serialized_irecv_data25   serialized_irecv_data(const communicator& comm, T& value)
26     : m_ia(comm), m_value(value) {}
27 
deserializeboost::mpi::detail::serialized_irecv_data28   void deserialize(status& stat)
29   {
30     m_ia >> m_value;
31     stat.m_count = 1;
32   }
33 
34   std::size_t     m_count;
35   packed_iarchive m_ia;
36   T&              m_value;
37 };
38 
39 template<>
40 struct serialized_irecv_data<packed_iarchive>
41 {
serialized_irecv_databoost::mpi::detail::serialized_irecv_data42   serialized_irecv_data(communicator const&, packed_iarchive& ia) : m_ia(ia) { }
43 
deserializeboost::mpi::detail::serialized_irecv_data44   void deserialize(status&) { /* Do nothing. */ }
45 
46   std::size_t      m_count;
47   packed_iarchive& m_ia;
48 };
49 
50 /**
51  * Internal data structure that stores everything required to manage
52  * the receipt of an array of serialized data via a request object.
53  */
54 template<typename T>
55 struct serialized_array_irecv_data
56 {
serialized_array_irecv_databoost::mpi::detail::serialized_array_irecv_data57   serialized_array_irecv_data(const communicator& comm, T* values, int n)
58     : m_count(0), m_ia(comm), m_values(values), m_nb(n) {}
59 
60   void deserialize(status& stat);
61 
62   std::size_t     m_count;
63   packed_iarchive m_ia;
64   T*              m_values;
65   int             m_nb;
66 };
67 
68 template<typename T>
deserialize(status & stat)69 void serialized_array_irecv_data<T>::deserialize(status& stat)
70 {
71   T* v = m_values;
72   T* end =  m_values+m_nb;
73   while (v < end) {
74     m_ia >> *v++;
75   }
76   stat.m_count = m_nb;
77 }
78 
79 /**
80  * Internal data structure that stores everything required to manage
81  * the receipt of an array of primitive data but unknown size.
82  * Such an array can have been send with blocking operation and so must
83  * be compatible with the (size_t,raw_data[]) format.
84  */
85 template<typename T, class A>
86 struct dynamic_array_irecv_data
87 {
88   BOOST_STATIC_ASSERT_MSG(is_mpi_datatype<T>::value, "Can only be specialized for MPI datatypes.");
89 
dynamic_array_irecv_databoost::mpi::detail::dynamic_array_irecv_data90   dynamic_array_irecv_data(std::vector<T,A>& values)
91     : m_count(-1), m_values(values) {}
92 
93   std::size_t       m_count;
94   std::vector<T,A>& m_values;
95 };
96 
97 template<typename T>
98 struct serialized_irecv_data<const skeleton_proxy<T> >
99 {
serialized_irecv_databoost::mpi::detail::serialized_irecv_data100   serialized_irecv_data(const communicator& comm, skeleton_proxy<T> proxy)
101     : m_isa(comm), m_ia(m_isa.get_skeleton()), m_proxy(proxy) { }
102 
deserializeboost::mpi::detail::serialized_irecv_data103   void deserialize(status& stat)
104   {
105     m_isa >> m_proxy.object;
106     stat.m_count = 1;
107   }
108 
109   std::size_t              m_count;
110   packed_skeleton_iarchive m_isa;
111   packed_iarchive&         m_ia;
112   skeleton_proxy<T>        m_proxy;
113 };
114 
115 template<typename T>
116 struct serialized_irecv_data<skeleton_proxy<T> >
117   : public serialized_irecv_data<const skeleton_proxy<T> >
118 {
119   typedef serialized_irecv_data<const skeleton_proxy<T> > inherited;
120 
serialized_irecv_databoost::mpi::detail::serialized_irecv_data121   serialized_irecv_data(const communicator& comm, const skeleton_proxy<T>& proxy)
122     : inherited(comm, proxy) { }
123 };
124 }
125 
126 #if BOOST_MPI_VERSION >= 3
127 template<class Data>
128 class request::probe_handler
129   : public request::handler,
130     protected Data {
131 
132 protected:
133   template<typename I1>
probe_handler(communicator const & comm,int source,int tag,I1 & i1)134   probe_handler(communicator const& comm, int source, int tag, I1& i1)
135     : Data(comm, i1),
136       m_comm(comm),
137       m_source(source),
138       m_tag(tag) {}
139   // no variadic template for now
140   template<typename I1, typename I2>
probe_handler(communicator const & comm,int source,int tag,I1 & i1,I2 & i2)141   probe_handler(communicator const& comm, int source, int tag, I1& i1, I2& i2)
142     : Data(comm, i1, i2),
143       m_comm(comm),
144       m_source(source),
145       m_tag(tag) {}
146 
147 public:
active() const148   bool active() const { return m_source != MPI_PROC_NULL; }
trivial()149   optional<MPI_Request&> trivial() { return boost::none; }
cancel()150   void cancel() { m_source = MPI_PROC_NULL; }
151 
wait()152   status wait() {
153     MPI_Message msg;
154     status stat;
155     BOOST_MPI_CHECK_RESULT(MPI_Mprobe, (m_source,m_tag,m_comm,&msg,&stat.m_status));
156     return unpack(msg, stat);
157   }
158 
test()159   optional<status> test() {
160     status stat;
161     int flag = 0;
162     MPI_Message msg;
163     BOOST_MPI_CHECK_RESULT(MPI_Improbe, (m_source,m_tag,m_comm,&flag,&msg,&stat.m_status));
164     if (flag) {
165       return unpack(msg, stat);
166     } else {
167       return optional<status>();
168     }
169   }
170 
171 protected:
172   friend class request;
173 
unpack(MPI_Message & msg,status & stat)174   status unpack(MPI_Message& msg, status& stat) {
175     int count;
176     MPI_Datatype datatype = this->Data::datatype();
177     BOOST_MPI_CHECK_RESULT(MPI_Get_count, (&stat.m_status, datatype, &count));
178     this->Data::resize(count);
179     BOOST_MPI_CHECK_RESULT(MPI_Mrecv, (this->Data::buffer(), count, datatype, &msg, &stat.m_status));
180     this->Data::deserialize();
181     m_source = MPI_PROC_NULL;
182     stat.m_count = 1;
183     return stat;
184   }
185 
186   communicator const& m_comm;
187   int m_source;
188   int m_tag;
189 };
190 #endif // BOOST_MPI_VERSION >= 3
191 
192 namespace detail {
193 template<class A>
194 struct dynamic_primitive_array_data {
dynamic_primitive_array_databoost::mpi::detail::dynamic_primitive_array_data195   dynamic_primitive_array_data(communicator const&, A& arr) : m_buffer(arr) {}
196 
bufferboost::mpi::detail::dynamic_primitive_array_data197   void* buffer() { return m_buffer.data(); }
resizeboost::mpi::detail::dynamic_primitive_array_data198   void  resize(std::size_t sz) { m_buffer.resize(sz); }
deserializeboost::mpi::detail::dynamic_primitive_array_data199   void  deserialize() {}
datatypeboost::mpi::detail::dynamic_primitive_array_data200   MPI_Datatype datatype() { return get_mpi_datatype<typename A::value_type>(); }
201 
202   A& m_buffer;
203 };
204 
205 template<typename T>
206 struct serialized_data {
serialized_databoost::mpi::detail::serialized_data207   serialized_data(communicator const& comm, T& value) : m_archive(comm), m_value(value) {}
208 
bufferboost::mpi::detail::serialized_data209   void* buffer() { return m_archive.address(); }
resizeboost::mpi::detail::serialized_data210   void  resize(std::size_t sz) { m_archive.resize(sz); }
deserializeboost::mpi::detail::serialized_data211   void  deserialize() { m_archive >> m_value; }
datatypeboost::mpi::detail::serialized_data212   MPI_Datatype datatype() { return MPI_PACKED; }
213 
214   packed_iarchive m_archive;
215   T& m_value;
216 };
217 
218 template<>
219 struct serialized_data<packed_iarchive> {
serialized_databoost::mpi::detail::serialized_data220   serialized_data(communicator const& comm, packed_iarchive& ar) : m_archive(ar) {}
221 
bufferboost::mpi::detail::serialized_data222   void* buffer() { return m_archive.address(); }
resizeboost::mpi::detail::serialized_data223   void  resize(std::size_t sz) { m_archive.resize(sz); }
deserializeboost::mpi::detail::serialized_data224   void  deserialize() {}
datatypeboost::mpi::detail::serialized_data225   MPI_Datatype datatype() { return MPI_PACKED; }
226 
227   packed_iarchive& m_archive;
228 };
229 
230 template<typename T>
231 struct serialized_data<const skeleton_proxy<T> > {
serialized_databoost::mpi::detail::serialized_data232   serialized_data(communicator const& comm, skeleton_proxy<T> skel)
233     : m_proxy(skel),
234       m_archive(comm) {}
235 
bufferboost::mpi::detail::serialized_data236   void* buffer() { return m_archive.get_skeleton().address(); }
resizeboost::mpi::detail::serialized_data237   void  resize(std::size_t sz) { m_archive.get_skeleton().resize(sz); }
deserializeboost::mpi::detail::serialized_data238   void  deserialize() { m_archive >> m_proxy.object; }
datatypeboost::mpi::detail::serialized_data239   MPI_Datatype datatype() { return MPI_PACKED; }
240 
241   skeleton_proxy<T> m_proxy;
242   packed_skeleton_iarchive m_archive;
243 };
244 
245 template<typename T>
246 struct serialized_data<skeleton_proxy<T> >
247   : public serialized_data<const skeleton_proxy<T> > {
248   typedef serialized_data<const skeleton_proxy<T> > super;
serialized_databoost::mpi::detail::serialized_data249   serialized_data(communicator const& comm, skeleton_proxy<T> skel)
250     : super(comm, skel) {}
251 };
252 
253 template<typename T>
254 struct serialized_array_data {
serialized_array_databoost::mpi::detail::serialized_array_data255   serialized_array_data(communicator const& comm, T* values, int nb)
256     : m_archive(comm), m_values(values), m_nb(nb) {}
257 
bufferboost::mpi::detail::serialized_array_data258   void* buffer() { return m_archive.address(); }
resizeboost::mpi::detail::serialized_array_data259   void  resize(std::size_t sz) { m_archive.resize(sz); }
deserializeboost::mpi::detail::serialized_array_data260   void  deserialize() {
261     T* end = m_values + m_nb;
262     T* v = m_values;
263     while (v != end) {
264       m_archive >> *v++;
265     }
266   }
datatypeboost::mpi::detail::serialized_array_data267   MPI_Datatype datatype() { return MPI_PACKED; }
268 
269   packed_iarchive m_archive;
270   T*  m_values;
271   int m_nb;
272 };
273 
274 }
275 
276 class BOOST_MPI_DECL request::legacy_handler : public request::handler {
277 public:
278   legacy_handler(communicator const& comm, int source, int tag);
279 
cancel()280   void cancel() {
281     for (int i = 0; i < 2; ++i) {
282       if (m_requests[i] != MPI_REQUEST_NULL) {
283         BOOST_MPI_CHECK_RESULT(MPI_Cancel, (m_requests+i));
284       }
285     }
286   }
287 
288   bool active() const;
289   optional<MPI_Request&> trivial();
290 
291   MPI_Request      m_requests[2];
292   communicator     m_comm;
293   int              m_source;
294   int              m_tag;
295 };
296 
297 template<typename T>
298 class request::legacy_serialized_handler
299   : public request::legacy_handler,
300     protected detail::serialized_irecv_data<T> {
301 public:
302   typedef detail::serialized_irecv_data<T> extra;
legacy_serialized_handler(communicator const & comm,int source,int tag,T & value)303   legacy_serialized_handler(communicator const& comm, int source, int tag, T& value)
304     : legacy_handler(comm, source, tag),
305       extra(comm, value)  {
306     BOOST_MPI_CHECK_RESULT(MPI_Irecv,
307 			   (&this->extra::m_count, 1,
308 			    get_mpi_datatype(this->extra::m_count),
309 			    source, tag, comm, m_requests+0));
310 
311   }
312 
wait()313   status wait() {
314     status stat;
315     if (m_requests[1] == MPI_REQUEST_NULL) {
316       // Wait for the count message to complete
317       BOOST_MPI_CHECK_RESULT(MPI_Wait,
318                              (m_requests, &stat.m_status));
319       // Resize our buffer and get ready to receive its data
320       this->extra::m_ia.resize(this->extra::m_count);
321       BOOST_MPI_CHECK_RESULT(MPI_Irecv,
322                              (this->extra::m_ia.address(), this->extra::m_ia.size(), MPI_PACKED,
323                               stat.source(), stat.tag(),
324                               MPI_Comm(m_comm), m_requests + 1));
325     }
326 
327     // Wait until we have received the entire message
328     BOOST_MPI_CHECK_RESULT(MPI_Wait,
329                            (m_requests + 1, &stat.m_status));
330 
331     this->deserialize(stat);
332     return stat;
333   }
334 
test()335   optional<status> test() {
336     status stat;
337     int flag = 0;
338 
339     if (m_requests[1] == MPI_REQUEST_NULL) {
340       // Check if the count message has completed
341       BOOST_MPI_CHECK_RESULT(MPI_Test,
342                              (m_requests, &flag, &stat.m_status));
343       if (flag) {
344         // Resize our buffer and get ready to receive its data
345         this->extra::m_ia.resize(this->extra::m_count);
346         BOOST_MPI_CHECK_RESULT(MPI_Irecv,
347                                (this->extra::m_ia.address(), this->extra::m_ia.size(),MPI_PACKED,
348                                 stat.source(), stat.tag(),
349                                 MPI_Comm(m_comm), m_requests + 1));
350       } else
351         return optional<status>(); // We have not finished yet
352     }
353 
354     // Check if we have received the message data
355     BOOST_MPI_CHECK_RESULT(MPI_Test,
356                            (m_requests + 1, &flag, &stat.m_status));
357     if (flag) {
358       this->deserialize(stat);
359       return stat;
360     } else
361       return optional<status>();
362   }
363 };
364 
365 template<typename T>
366 class request::legacy_serialized_array_handler
367   : public    request::legacy_handler,
368     protected detail::serialized_array_irecv_data<T> {
369   typedef detail::serialized_array_irecv_data<T> extra;
370 
371 public:
legacy_serialized_array_handler(communicator const & comm,int source,int tag,T * values,int n)372   legacy_serialized_array_handler(communicator const& comm, int source, int tag, T* values, int n)
373     : legacy_handler(comm, source, tag),
374       extra(comm, values, n) {
375     BOOST_MPI_CHECK_RESULT(MPI_Irecv,
376                            (&this->extra::m_count, 1,
377                             get_mpi_datatype(this->extra::m_count),
378                             source, tag, comm, m_requests+0));
379   }
380 
wait()381   status wait() {
382     status stat;
383     if (m_requests[1] == MPI_REQUEST_NULL) {
384       // Wait for the count message to complete
385       BOOST_MPI_CHECK_RESULT(MPI_Wait,
386                              (m_requests, &stat.m_status));
387       // Resize our buffer and get ready to receive its data
388       this->extra::m_ia.resize(this->extra::m_count);
389       BOOST_MPI_CHECK_RESULT(MPI_Irecv,
390                              (this->extra::m_ia.address(), this->extra::m_ia.size(), MPI_PACKED,
391                               stat.source(), stat.tag(),
392                               MPI_Comm(m_comm), m_requests + 1));
393     }
394 
395     // Wait until we have received the entire message
396     BOOST_MPI_CHECK_RESULT(MPI_Wait,
397                            (m_requests + 1, &stat.m_status));
398 
399     this->deserialize(stat);
400     return stat;
401   }
402 
test()403   optional<status> test() {
404     status stat;
405     int flag = 0;
406 
407     if (m_requests[1] == MPI_REQUEST_NULL) {
408       // Check if the count message has completed
409       BOOST_MPI_CHECK_RESULT(MPI_Test,
410                              (m_requests, &flag, &stat.m_status));
411       if (flag) {
412         // Resize our buffer and get ready to receive its data
413         this->extra::m_ia.resize(this->extra::m_count);
414         BOOST_MPI_CHECK_RESULT(MPI_Irecv,
415                                (this->extra::m_ia.address(), this->extra::m_ia.size(),MPI_PACKED,
416                                 stat.source(), stat.tag(),
417                                 MPI_Comm(m_comm), m_requests + 1));
418       } else
419         return optional<status>(); // We have not finished yet
420     }
421 
422     // Check if we have received the message data
423     BOOST_MPI_CHECK_RESULT(MPI_Test,
424                            (m_requests + 1, &flag, &stat.m_status));
425     if (flag) {
426       this->deserialize(stat);
427       return stat;
428     } else
429       return optional<status>();
430   }
431 };
432 
433 template<typename T, class A>
434 class request::legacy_dynamic_primitive_array_handler
435   : public request::legacy_handler,
436     protected detail::dynamic_array_irecv_data<T,A>
437 {
438   typedef detail::dynamic_array_irecv_data<T,A> extra;
439 
440 public:
legacy_dynamic_primitive_array_handler(communicator const & comm,int source,int tag,std::vector<T,A> & values)441   legacy_dynamic_primitive_array_handler(communicator const& comm, int source, int tag, std::vector<T,A>& values)
442     : legacy_handler(comm, source, tag),
443       extra(values) {
444     BOOST_MPI_CHECK_RESULT(MPI_Irecv,
445                            (&this->extra::m_count, 1,
446                             get_mpi_datatype(this->extra::m_count),
447                             source, tag, comm, m_requests+0));
448   }
449 
wait()450   status wait() {
451     status stat;
452     if (m_requests[1] == MPI_REQUEST_NULL) {
453       // Wait for the count message to complete
454       BOOST_MPI_CHECK_RESULT(MPI_Wait,
455                              (m_requests, &stat.m_status));
456       // Resize our buffer and get ready to receive its data
457       this->extra::m_values.resize(this->extra::m_count);
458       BOOST_MPI_CHECK_RESULT(MPI_Irecv,
459                              (detail::c_data(this->extra::m_values), this->extra::m_values.size(), get_mpi_datatype<T>(),
460                               stat.source(), stat.tag(),
461                               MPI_Comm(m_comm), m_requests + 1));
462     }
463     // Wait until we have received the entire message
464     BOOST_MPI_CHECK_RESULT(MPI_Wait,
465                            (m_requests + 1, &stat.m_status));
466     return stat;
467   }
468 
test()469   optional<status> test() {
470     status stat;
471     int flag = 0;
472 
473     if (m_requests[1] == MPI_REQUEST_NULL) {
474       // Check if the count message has completed
475       BOOST_MPI_CHECK_RESULT(MPI_Test,
476                              (m_requests, &flag, &stat.m_status));
477       if (flag) {
478         // Resize our buffer and get ready to receive its data
479         this->extra::m_values.resize(this->extra::m_count);
480         BOOST_MPI_CHECK_RESULT(MPI_Irecv,
481                                (detail::c_data(this->extra::m_values), this->extra::m_values.size(), get_mpi_datatype<T>(),
482                                 stat.source(), stat.tag(),
483                                 MPI_Comm(m_comm), m_requests + 1));
484       } else
485         return optional<status>(); // We have not finished yet
486     }
487 
488     // Check if we have received the message data
489     BOOST_MPI_CHECK_RESULT(MPI_Test,
490                            (m_requests + 1, &flag, &stat.m_status));
491     if (flag) {
492       return stat;
493     } else
494       return optional<status>();
495   }
496 };
497 
498 class BOOST_MPI_DECL request::trivial_handler : public request::handler {
499 
500 public:
501   trivial_handler();
502 
503   status wait();
504   optional<status> test();
505   void cancel();
506 
507   bool active() const;
508   optional<MPI_Request&> trivial();
509 
510 private:
511   friend class request;
512   MPI_Request      m_request;
513 };
514 
515 class request::dynamic_handler : public request::handler {
516   dynamic_handler();
517 
518   status wait();
519   optional<status> test();
520   void cancel();
521 
522   bool active() const;
523   optional<MPI_Request&> trivial();
524 
525 private:
526   friend class request;
527   MPI_Request      m_requests[2];
528 };
529 
530 template<typename T>
make_serialized(communicator const & comm,int source,int tag,T & value)531 request request::make_serialized(communicator const& comm, int source, int tag, T& value) {
532 #if defined(BOOST_MPI_USE_IMPROBE)
533   return request(new probe_handler<detail::serialized_data<T> >(comm, source, tag, value));
534 #else
535   return request(new legacy_serialized_handler<T>(comm, source, tag, value));
536 #endif
537 }
538 
539 template<typename T>
make_serialized_array(communicator const & comm,int source,int tag,T * values,int n)540 request request::make_serialized_array(communicator const& comm, int source, int tag, T* values, int n) {
541 #if defined(BOOST_MPI_USE_IMPROBE)
542   return request(new probe_handler<detail::serialized_array_data<T> >(comm, source, tag, values, n));
543 #else
544   return request(new legacy_serialized_array_handler<T>(comm, source, tag, values, n));
545 #endif
546 }
547 
548 template<typename T, class A>
make_dynamic_primitive_array_recv(communicator const & comm,int source,int tag,std::vector<T,A> & values)549 request request::make_dynamic_primitive_array_recv(communicator const& comm, int source, int tag,
550                                                    std::vector<T,A>& values) {
551 #if defined(BOOST_MPI_USE_IMPROBE)
552   return request(new probe_handler<detail::dynamic_primitive_array_data<std::vector<T,A> > >(comm,source,tag,values));
553 #else
554   return request(new legacy_dynamic_primitive_array_handler<T,A>(comm, source, tag, values));
555 #endif
556 }
557 
558 template<typename T>
559 request
make_trivial_send(communicator const & comm,int dest,int tag,T const * values,int n)560 request::make_trivial_send(communicator const& comm, int dest, int tag, T const* values, int n) {
561   trivial_handler* handler = new trivial_handler;
562   BOOST_MPI_CHECK_RESULT(MPI_Isend,
563                          (const_cast<T*>(values), n,
564                           get_mpi_datatype<T>(),
565                           dest, tag, comm, &handler->m_request));
566   return request(handler);
567 }
568 
569 template<typename T>
570 request
make_trivial_send(communicator const & comm,int dest,int tag,T const & value)571 request::make_trivial_send(communicator const& comm, int dest, int tag, T const& value) {
572   return make_trivial_send(comm, dest, tag, &value, 1);
573 }
574 
575 template<typename T>
576 request
make_trivial_recv(communicator const & comm,int dest,int tag,T * values,int n)577 request::make_trivial_recv(communicator const& comm, int dest, int tag, T* values, int n) {
578   trivial_handler* handler = new trivial_handler;
579   BOOST_MPI_CHECK_RESULT(MPI_Irecv,
580                          (values, n,
581                           get_mpi_datatype<T>(),
582                           dest, tag, comm, &handler->m_request));
583   return request(handler);
584 }
585 
586 template<typename T>
587 request
make_trivial_recv(communicator const & comm,int dest,int tag,T & value)588 request::make_trivial_recv(communicator const& comm, int dest, int tag, T& value) {
589   return make_trivial_recv(comm, dest, tag, &value, 1);
590 }
591 
592 template<typename T, class A>
make_dynamic_primitive_array_send(communicator const & comm,int dest,int tag,std::vector<T,A> const & values)593 request request::make_dynamic_primitive_array_send(communicator const& comm, int dest, int tag,
594                                                    std::vector<T,A> const& values) {
595 #if defined(BOOST_MPI_USE_IMPROBE)
596   return make_trivial_send(comm, dest, tag, values.data(), values.size());
597 #else
598   {
599     // non blocking recv by legacy_dynamic_primitive_array_handler
600     // blocking recv by status recv_vector(source,tag,value,primitive)
601     boost::shared_ptr<std::size_t> size(new std::size_t(values.size()));
602     dynamic_handler* handler = new dynamic_handler;
603     request req(handler);
604     req.preserve(size);
605 
606     BOOST_MPI_CHECK_RESULT(MPI_Isend,
607                            (size.get(), 1,
608                             get_mpi_datatype(*size),
609                             dest, tag, comm, handler->m_requests+0));
610     BOOST_MPI_CHECK_RESULT(MPI_Isend,
611                            (const_cast<T*>(values.data()), *size,
612                             get_mpi_datatype<T>(),
613                             dest, tag, comm, handler->m_requests+1));
614     return req;
615   }
616 #endif
617 }
618 
619 inline
legacy_handler(communicator const & comm,int source,int tag)620 request::legacy_handler::legacy_handler(communicator const& comm, int source, int tag)
621   : m_comm(comm),
622     m_source(source),
623     m_tag(tag)
624 {
625   m_requests[0] = MPI_REQUEST_NULL;
626   m_requests[1] = MPI_REQUEST_NULL;
627 }
628 
629 }}
630 
631 #endif // BOOST_MPI_REQUEST_HANDLERS_HPP
632