request.C
Go to the documentation of this file.
1 // The libMesh Finite Element Library.
2 // Copyright (C) 2002-2018 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
3 
4 // This library is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU Lesser General Public
6 // License as published by the Free Software Foundation; either
7 // version 2.1 of the License, or (at your option) any later version.
8 
9 // This library is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 // Lesser General Public License for more details.
13 
14 // You should have received a copy of the GNU Lesser General Public
15 // License along with this library; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 
18 // Local includes
19 #include "libmesh/request.h"
20 
21 // Parallel includes
23 #include "libmesh/post_wait_work.h"
24 #include "libmesh/status.h"
25 
26 // libMesh Includes
27 #include "libmesh/libmesh_common.h"
29 
30 // C++ includes
31 #include <memory>
32 #include <vector>
33 #include <utility>
34 
35 
36 namespace libMesh
37 {
38 
39 namespace Parallel
40 {
41 
42 // ------------------------------------------------------------
43 // Request member functions
45 #ifdef LIBMESH_HAVE_MPI
46  _request(MPI_REQUEST_NULL),
47 #else
48  _request(),
49 #endif
50  post_wait_work(nullptr)
51 {}
52 
54  _request(r),
55  post_wait_work(nullptr)
56 {}
57 
58 Request::Request (const Request & other) :
59  _request(other._request),
60  post_wait_work(other.post_wait_work)
61 {
62  if (other._prior_request.get())
63  _prior_request = std::unique_ptr<Request>
64  (new Request(*other._prior_request.get()));
65 
66  // operator= should behave like a shared pointer
67  if (post_wait_work)
68  post_wait_work->second++;
69 }
70 
72 {
73  if (post_wait_work)
74  {
75  // Decrement the use count
76  post_wait_work->second--;
77 
78  if (!post_wait_work->second)
79  {
80 #ifdef DEBUG
81  // If we're done using this request, then we'd better have
82  // done the work we waited for
83  for (const auto & item : post_wait_work->first)
84  libmesh_assert(!item);
85 #endif
86  delete post_wait_work;
87  post_wait_work = nullptr;
88  }
89  }
90 }
91 
93 {
94  this->cleanup();
95  _request = other._request;
97 
98  if (other._prior_request.get())
99  _prior_request = std::unique_ptr<Request>
100  (new Request(*other._prior_request.get()));
101 
102  // operator= should behave like a shared pointer
103  if (post_wait_work)
104  post_wait_work->second++;
105 
106  return *this;
107 }
108 
110 {
111  this->cleanup();
112  _request = r;
113  post_wait_work = nullptr;
114  return *this;
115 }
116 
118  this->cleanup();
119 }
120 
122 {
123  LOG_SCOPE("wait()", "Parallel::Request");
124 
125  if (_prior_request.get())
126  {
127  _prior_request->wait();
128  _prior_request.reset(nullptr);
129  }
130 
131  Status stat;
132 #ifdef LIBMESH_HAVE_MPI
133  libmesh_call_mpi
134  (MPI_Wait (&_request, stat.get()));
135 #endif
136  if (post_wait_work)
137  {
138  for (auto & item : post_wait_work->first)
139  {
140  // The user should never try to give us non-existent work or try
141  // to wait() twice.
142  libmesh_assert (item);
143  item->run();
144  delete item;
145  item = nullptr;
146  }
147  post_wait_work->first.clear();
148  }
149 
150  return stat;
151 }
152 
154 {
155 #ifdef LIBMESH_HAVE_MPI
156  int val=0;
157 
158  libmesh_call_mpi
159  (MPI_Test (&_request, &val, MPI_STATUS_IGNORE));
160 
161  if (val)
162  {
163  libmesh_assert (_request == MPI_REQUEST_NULL);
164  libmesh_assert_equal_to (val, 1);
165  }
166 
167  return val;
168 #else
169  return true;
170 #endif
171 }
172 
173 #ifdef LIBMESH_HAVE_MPI
174 bool Request::test (status & stat)
175 {
176  int val=0;
177 
178  libmesh_call_mpi
179  (MPI_Test (&_request, &val, &stat));
180 
181  return val;
182 }
183 #else
184 bool Request::test (status &)
185 {
186  return true;
187 }
188 #endif
189 
191 {
192  // We're making a chain of prior requests, not a tree
193  libmesh_assert(!req._prior_request.get());
194 
195  Request * new_prior_req = new Request(req);
196 
197  // new_prior_req takes ownership of our existing _prior_request
198  new_prior_req->_prior_request.reset(this->_prior_request.release());
199 
200  // Our _prior_request now manages the new resource we just set up
201  this->_prior_request.reset(new_prior_req);
202 }
203 
205 {
206  if (!post_wait_work)
207  post_wait_work = new
208  std::pair<std::vector <PostWaitWork * >, unsigned int>
209  (std::vector <PostWaitWork * >(), 1);
210  post_wait_work->first.push_back(work);
211 }
212 
213 void wait (std::vector<Request> & r)
214 {
215  for (std::size_t i=0; i<r.size(); i++)
216  r[i].wait();
217 }
218 
219 std::size_t waitany (std::vector<Request> & r)
220 {
221  libmesh_assert(!r.empty());
222 
223  int index = 0;
224  int r_size = cast_int<int>(r.size());
225  std::vector<request> raw(r_size);
226  for (int i=0; i != r_size; ++i)
227  {
228  Request * root = &r[i];
229  // If we have prior requests, we need to complete the first one
230  // first
231  while (root->_prior_request.get())
232  root = root->_prior_request.get();
233  raw[i] = *root->get();
234  }
235 
236  bool only_priors_completed = false;
237 
238  Request * next;
239 
240  do
241  {
242 #ifdef LIBMESH_HAVE_MPI
243  libmesh_call_mpi
244  (MPI_Waitany(r_size, raw.data(), &index, MPI_STATUS_IGNORE));
245 #endif
246 
247  Request * completed = &r[index];
248  next = completed;
249 
250  // If we completed a prior request, we're not really done yet,
251  // so find the next in that line to try again.
252  while (completed->_prior_request.get())
253  {
254  only_priors_completed = true;
255  next = completed;
256  completed = completed->_prior_request.get();
257  }
258 
259  // Do any post-wait work for the completed request
260  if (completed->post_wait_work)
261  for (auto & item : completed->post_wait_work->first)
262  {
263  // The user should never try to give us non-existent work or try
264  // to wait() twice.
265  libmesh_assert (item);
266  item->run();
267  delete item;
268  item = nullptr;
269  }
270 
271  next->_prior_request.reset(nullptr);
272  raw[index] = *next->get();
273 
274  } while(only_priors_completed);
275 
276  return index;
277 }
278 
279 } // namespace Parallel
280 
281 } // namespace libMesh
void wait(std::vector< Request > &r)
Definition: request.C:213
std::pair< std::vector< PostWaitWork *>, unsigned int > * post_wait_work
Definition: request.h:118
void add_prior_request(const Request &req)
Definition: request.C:190
static const unsigned int next[3]
std::size_t waitany(std::vector< Request > &r)
Definition: request.C:219
Request & operator=(const Request &other)
Definition: request.C:92
void add_post_wait_work(PostWaitWork *work)
Definition: request.C:204
std::unique_ptr< Request > _prior_request
Definition: request.h:110