KaMPIng 0.1.0
(Near) zero-overhead C++ MPI bindings.
Loading...
Searching...
No Matches
request.hpp
1// This file is part of KaMPIng.
2//
3// Copyright 2023 The KaMPIng Authors
4//
5// KaMPIng is free software : you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
6// License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later
7// version. KaMPIng is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
8// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
9// for more details.
10//
11// You should have received a copy of the GNU Lesser General Public License along with KaMPIng. If not, see
12// <https://www.gnu.org/licenses/>.
13
14#pragma once
15
16#include <optional>
17
18#include <mpi.h>
19
22#include "kamping/named_parameters_detail/status_parameters.hpp"
24#include "kamping/result.hpp"
25
26namespace kamping {
27
28/// @brief Base class for request wrappers.
29///
30/// This class provides the common interface for all request wrappers. It is
31/// not intended to be used directly. Instead, use \ref kamping::Request or
32/// \ref kamping::PooledRequest or define your own request type, which must
33/// implement \c request_ptr().
34///
35/// @tparam RequestType The derived type.
36template <typename RequestType>
38public:
39 constexpr RequestBase() = default;
40 ~RequestBase() = default;
41
42 /// @brief Copy constructor is deleted because requests should only be moved.
43 RequestBase(RequestBase const&) = delete;
44 /// @brief Copy assignment operator is deleted because requests should only be moved.
46 /// @brief Move constructor.
48 /// @brief Move assignment operator.
50
51private:
52 ///@brief returns a pointer to the wrapped MPI_Request by calling \c request_ptr() on \ref RequestType using CRTP.
53 MPI_Request* request_ptr() {
54 return static_cast<RequestType&>(*this).request_ptr();
55 }
56 ///@brief returns a const pointer to the wrapped MPI_Request by calling \c request_ptr() on \ref RequestType using
57 /// CRTP.
58 MPI_Request const* request_ptr() const {
59 return static_cast<RequestType const&>(*this).request_ptr();
60 }
61
62public:
63 /// @brief Returns when the operation defined by the underlying request completes.
64 /// If the underlying request was initialized by a non-blocking communication call, it is set to \c
65 /// MPI_REQUEST_NULL.
66 ///
67 /// @param status_param A parameter created by \ref kamping::status() or \ref kamping::status_out().
68 /// Defaults to \c kamping::status(ignore<>).
69 ///
70 /// @return The status object, if \p status is \ref kamping::status_out(), otherwise nothing.
71 template <typename StatusParamObjectType = decltype(status(ignore<>))>
73 static_assert(
74 StatusParamObjectType::parameter_type == internal::ParameterType::status,
75 "Only status parameters are allowed."
76 );
77 auto status = status_param.construct_buffer_or_rebind();
80 if constexpr (internal::is_extractable<StatusParamObjectType>) {
81 return status.extract();
82 }
83 }
84
85 /// @return True if this request is equal to \c MPI_REQUEST_NULL.
86 [[nodiscard]] bool is_null() const {
87 return *request_ptr() == MPI_REQUEST_NULL;
88 }
89
90 /// @brief Tests for completion of the underlying request. If the underlying request was
91 /// initialized by a non-blocking communication call and completes, it is set to \c MPI_REQUEST_NULL.
92 ///
93 /// @param status_param A parameter created by \ref kamping::status() or \ref kamping::status_out().
94 /// Defaults to \c kamping::status(ignore<>).
95 ///
96 /// @return Returns \c true if the underlying request is complete. If \p status is \ref kamping::status_out() and
97 /// owning, returns an \c std::optional encapsulating the status in case of completion, \c std::nullopt otherwise.
98 template <typename StatusParamObjectType = decltype(status(ignore<>))>
100 static_assert(
101 StatusParamObjectType::parameter_type == internal::ParameterType::status,
102 "Only status parameters are allowed."
103 );
104 auto status = status_param.construct_buffer_or_rebind();
105 int is_finished;
108 if constexpr (internal::is_extractable<StatusParamObjectType>) {
109 if (is_finished) {
110 return std::optional{status.extract()};
111 } else {
112 return std::optional<Status>{};
113 }
114 } else {
115 return static_cast<bool>(is_finished);
116 }
117 }
118
119 /// @return A reference to the underlying MPI_Request handle.
121 return *request_ptr();
122 }
123
124 /// @return A reference to the underlying MPI_Request handle.
125 [[nodiscard]] MPI_Request const& mpi_request() const {
126 return *request_ptr();
127 }
128
129 // TODO: request cancellation and querying of cancellation status
130
131 /// @return Returns \c true if the other request wrapper points to the same request.
132 template <typename T>
133 bool operator==(RequestBase<T> const& other) const {
134 return *request_ptr() == *other.request_ptr();
135 }
136
137 /// @return Returns \c true if the other request wrapper points to a different request.
138 template <typename T>
139 bool operator!=(RequestBase<T> const& other) const {
140 return !(*this == other);
141 }
142};
143
144/// @brief Wrapper for MPI request handles (aka. \c MPI_Request).
145class Request : public RequestBase<Request> {
146public:
147 /// @brief Constructs a request handle from an \c MPI_Request.
148 /// @param request The request to encapsulate. Defaults to \c MPI_REQUEST_NULL.
150
151 /// @brief returns a pointer to the wrapped MPI_Request.
153 return &_request;
154 }
155
156 /// @brief returns a const pointer to the wrapped MPI_Request.
157 MPI_Request const* request_ptr() const {
158 return &_request;
159 }
160
161private:
162 MPI_Request _request; ///< the encapsulated MPI_Request
163};
164
165/// @brief Wrapper for MPI requests owned by a \ref RequestPool.
166///
167/// @tparam IndexType type of the index of this request in the pool.
168template <typename IndexType>
169class PooledRequest : public RequestBase<PooledRequest<IndexType>> {
170public:
171 /// @brief constructs a \ref PooledRequest with the given index \p idx and \p request.
173
174 /// @brief returns a pointer to the wrapped MPI_Request.
176 return &_request;
177 }
178
179 /// @brief returns a const pointer to the wrapped MPI_Request.
180 MPI_Request const* request_ptr() const {
181 return &_request;
182 }
183
184 /// @brief provides access to this request's index in the pool.
185 IndexType index() const {
186 return _index;
187 }
188
189private:
190 IndexType _index; ///< the index
191 MPI_Request& _request; ///< the encapsulated request
192};
193
194namespace requests {
195
196/// @brief Waits for completion of all requests handles passed.
197/// @param requests A (contiguous) container of \c MPI_Request.
198/// @tparam Container The container type.
199template <
200 typename Container,
201 typename std::enable_if<
203 Container> && std::is_same_v<typename std::remove_reference_t<Container>::value_type, MPI_Request>,
204 bool>::type = true>
205void wait_all(Container&& requests) {
206 int err = MPI_Waitall(asserting_cast<int>(requests.size()), requests.data(), MPI_STATUSES_IGNORE);
207 THROW_IF_MPI_ERROR(err, MPI_Waitall);
208}
209
210/// @brief Waits for completion of all requests handles passed.
211/// Warning: This relies on undefined behavior!
212/// @param requests A (contiguous) container of \ref kamping::Request.
213/// @tparam Container The container type.
214template <
215 typename Container,
216 typename std::enable_if<
218 Container> && std::is_same_v<typename std::remove_reference_t<Container>::value_type, Request>,
219 bool>::type = true>
220void wait_all_with_undefined_behavior(Container&& requests) {
221 static_assert(
222 // "A pointer to a standard-layout class may be converted (with reinterpret_cast) to a pointer to its first
223 // non-static data member and vice versa." https://en.cppreference.com/w/cpp/types/is_standard_layout
224 sizeof(Request) == sizeof(MPI_Request) && std::is_standard_layout_v<Request>,
225 "Request is not layout compatible with MPI_Request."
226 );
227 // this is still undefined, we could cast a single pointer, but not an array
228 MPI_Request* begin = reinterpret_cast<MPI_Request*>(requests.data());
229 wait_all(Span<MPI_Request>{begin, requests.size()});
230}
231///
232/// @brief Waits for completion of all requests handles passed.
233/// This incurs overhead for copying the request handles to an intermediate container.
234/// @param requests A (contiguous) container of \ref kamping::Request.
235/// @tparam Container The container type.
236template <
237 typename Container,
238 typename std::enable_if<
240 Container> && std::is_same_v<typename std::remove_reference_t<Container>::value_type, Request>,
241 bool>::type = true>
242void wait_all(Container&& requests) {
243 std::vector<MPI_Request> mpi_requests(requests.size());
244 // we can not use the STL here, because we only check for the presence of .data()
245 auto begin = requests.data();
246 auto end = begin + requests.size();
247 size_t idx = 0;
248 for (auto current = begin; current != end; current++) {
249 mpi_requests[idx] = current->mpi_request();
250 idx++;
251 }
252 wait_all(mpi_requests);
253}
254
255/// @brief Wait for completion of all request handles passed.
256/// This incurs overhead for copying the request handles to an intermediate container.
257/// @param args A list of request handles to wait on. These may be lvalues or rvalues convertible \ref kamping::Request.
258template <
259 typename... RequestType,
260 typename =
261 std::enable_if_t<std::conjunction_v<std::is_convertible<std::remove_reference_t<RequestType>, Request>...>>>
262void wait_all(RequestType&&... args) {
263 constexpr size_t req_size = sizeof...(args);
264 MPI_Request reqs[req_size] = {Request{std::move(args)}.mpi_request()...};
265 wait_all(Span<MPI_Request>(reqs, req_size));
266}
267
268// TODO: wait_any, wait_same, test_all, test_any, test_some
269
270} // namespace requests
271} // namespace kamping
STL-compatible allocator for requesting memory using the builtin MPI allocator.
Definition allocator.hpp:32
Wrapper for MPI requests owned by a RequestPool.
Definition request.hpp:169
MPI_Request * request_ptr()
returns a pointer to the wrapped MPI_Request.
Definition request.hpp:175
IndexType index() const
provides access to this request's index in the pool.
Definition request.hpp:185
PooledRequest(IndexType idx, MPI_Request &request)
constructs a PooledRequest with the given index idx and request.
Definition request.hpp:172
MPI_Request const * request_ptr() const
returns a const pointer to the wrapped MPI_Request.
Definition request.hpp:180
Base class for request wrappers.
Definition request.hpp:37
RequestBase & operator=(RequestBase const &)=delete
Copy assignment operator is deleted because requests should only be moved.
bool is_null() const
Definition request.hpp:86
bool operator!=(RequestBase< T > const &other) const
Definition request.hpp:139
RequestBase(RequestBase &&)=default
Move constructor.
RequestBase(RequestBase const &)=delete
Copy constructor is deleted because requests should only be moved.
RequestBase & operator=(RequestBase &&)=default
Move assignment operator.
MPI_Request & mpi_request()
Definition request.hpp:120
MPI_Request const & mpi_request() const
Definition request.hpp:125
auto test(StatusParamObjectType status_param=kamping::status(ignore<>))
Tests for completion of the underlying request. If the underlying request was initialized by a non-bl...
Definition request.hpp:99
bool operator==(RequestBase< T > const &other) const
Definition request.hpp:133
auto wait(StatusParamObjectType status_param=kamping::status(ignore<>))
Returns when the operation defined by the underlying request completes. If the underlying request was...
Definition request.hpp:72
Wrapper for MPI request handles (aka. MPI_Request).
Definition request.hpp:145
MPI_Request const * request_ptr() const
returns a const pointer to the wrapped MPI_Request.
Definition request.hpp:157
Request(MPI_Request request=MPI_REQUEST_NULL)
Constructs a request handle from an MPI_Request.
Definition request.hpp:149
MPI_Request * request_ptr()
returns a pointer to the wrapped MPI_Request.
Definition request.hpp:152
Code for error handling.
#define THROW_IF_MPI_ERROR(error_code, function)
Wrapper around THROWING_KASSERT for MPI errors.
Definition error_handling.hpp:33
auto status(internal::ignore_t< void >)
pass MPI_STATUS_IGNORE to the underlying MPI call.
Definition status_parameters.hpp:52
auto request()
Internally allocate a request object and return it to the user.
Definition named_parameters.hpp:1122
@ status
Tag used to represent the status in a MPI call.
constexpr bool has_data_member_v
Boolean value helping to decide if data type has .data() method.
Definition data_buffer.hpp:248
static MPI_Status * status_param_to_native_ptr(StatusParam &param)
returns a pointer to the MPI_Status encapsulated by the provided status parameter object.
Definition parameter_objects.hpp:489
Parameter objects return by named parameter factory functions.
Some functions and types simplifying/enabling the development of wrapped MPI calls in KaMPIng.