Interface Documentation
Version: invalid
task_wrapper.hh
Go to the documentation of this file.
1 /*
2  @@@@@@@@ @@ @@@@@@ @@@@@@@@ @@
3  /@@///// /@@ @@////@@ @@////// /@@
4  /@@ /@@ @@@@@ @@ // /@@ /@@
5  /@@@@@@@ /@@ @@///@@/@@ /@@@@@@@@@/@@
6  /@@//// /@@/@@@@@@@/@@ ////////@@/@@
7  /@@ /@@/@@//// //@@ @@ /@@/@@
8  /@@ @@@//@@@@@@ //@@@@@@ @@@@@@@@ /@@
9  // /// ////// ////// //////// //
10 
11  Copyright (c) 2016, Triad National Security, LLC
12  All rights reserved.
13  */
14 #pragma once
15 
18 #include <flecsi-config.h>
19 
20 #if !defined(__FLECSI_PRIVATE__)
21 #error Do not include this file directly!
22 #endif
23 
28 #include "flecsi/run/backend.hh"
29 #include "flecsi/util/common.hh"
31 #include "flecsi/util/serialize.hh"
32 #include "unbind_accessors.hh"
33 #include <flecsi/flog.hh>
34 
35 #if !defined(FLECSI_ENABLE_LEGION)
36 #error FLECSI_ENABLE_LEGION not defined! This file depends on Legion!
37 #endif
38 
39 #include <legion.h>
40 
41 #include <string>
42 #include <utility>
43 
44 namespace flecsi {
45 
46 inline log::devel_tag task_wrapper_tag("task_wrapper");
47 
48 // Send and receive only the reference_base portion:
49 template<class T, std::size_t Priv>
50 struct util::serial_convert<data::accessor<data::dense, T, Priv>> {
52  using Rep = std::size_t;
53  static Rep put(const type & r) {
54  return r.identifier();
55  }
56  static type get(const Rep & r) {
57  return type(r);
58  }
59 };
60 template<class T, std::size_t Priv>
61 struct util::serial_convert<data::accessor<data::singular, T, Priv>> {
63  using Base = typename type::base_type;
64  static const Base & put(const type & a) {
65  return a.get_base();
66  }
67  static type get(Base b) {
68  return b;
69  }
70 };
71 // NB: topology_accessor is trivially copyable.
72 
73 template<class T>
75  using type = future<T>;
76  struct Rep {};
77  static Rep put(const type &) {
78  return {};
79  }
80  static type get(const Rep &) {
81  return {};
82  }
83 };
84 
85 namespace exec::leg {
86 using run::leg::task;
87 
88 namespace detail {
89 inline task_id_t last_task; // 0 is the top-level task
100 template<typename RETURN, task<RETURN> * TASK, std::size_t A>
101 void register_task();
102 
103 template<class T>
104 struct decay : std::decay<T> {};
105 template<class... TT>
106 struct decay<std::tuple<TT...>> {
107  using type = std::tuple<std::decay_t<TT>...>;
108 };
109 
110 template<class T>
111 auto
112 tuple_get(const Legion::Task & t) {
113  struct Check {
114  const std::byte *b, *e;
115  Check(const Legion::Task & t)
116  : b(static_cast<const std::byte *>(t.args)), e(b + t.arglen) {}
117  ~Check() {
118  flog_assert(b == e, "Bad Task::arglen");
119  }
120  } ch(t);
122 }
123 } // namespace detail
124 
133 template<auto & F, size_t A = loc | leaf>
134 // 'extern' works around GCC bug #90493
135 extern const task_id_t
136  task_id = (run::context::instance().register_init(detail::register_task<
137  typename util::function_traits<decltype(F)>::return_type,
138  F,
139  A>),
140  ++detail::last_task);
141 
142 template<typename RETURN, task<RETURN> * TASK, std::size_t A>
143 void
145  constexpr auto processor_type = mask_to_processor_type(A);
146  static_assert(processor_type != task_processor_type_t::mpi,
147  "Legion tasks cannot use MPI");
148 
149  const std::string name = util::symbol<*TASK>();
150  {
151  log::devel_guard guard(task_wrapper_tag);
152  flog_devel(info) << "registering pure Legion task " << name << std::endl;
153  }
154 
155  Legion::TaskVariantRegistrar registrar(task_id<*TASK, A>, name.c_str());
156  Legion::Processor::Kind kind = processor_type == task_processor_type_t::toc
157  ? Legion::Processor::TOC_PROC
158  : Legion::Processor::LOC_PROC;
159  registrar.add_constraint(Legion::ProcessorConstraint(kind));
160  registrar.set_leaf(leaf_task(A));
161  registrar.set_inner(inner_task(A));
162  registrar.set_idempotent(idempotent_task(A));
163 
164  /*
165  This section of conditionals is necessary because there is still
166  a distinction between void and non-void task registration with
167  Legion.
168  */
169 
170  if constexpr(std::is_same_v<RETURN, void>) {
171  Legion::Runtime::preregister_task_variant<TASK>(registrar, name.c_str());
172  }
173  else {
174  Legion::Runtime::preregister_task_variant<RETURN, TASK>(
175  registrar, name.c_str());
176  } // if
177 } // registration_callback
178 
179 // A trivial wrapper for nullary functions.
180 template<auto & F>
181 auto
182 verb(const Legion::Task *,
183  const std::vector<Legion::PhysicalRegion> &,
184  Legion::Context,
185  Legion::Runtime *) {
186  return F();
187 }
188 
199 template<auto & F, task_processor_type_t P> // P is for specialization only
200 struct task_wrapper {
201 
203  using RETURN = typename Traits::return_type;
204  using param_tuple = typename Traits::arguments_type;
205 
206  static constexpr task_processor_type_t LegionProcessor = P;
207 
212  static RETURN execute(const Legion::Task * task,
213  const std::vector<Legion::PhysicalRegion> & regions,
214  Legion::Context context,
215  Legion::Runtime * runtime) {
216  {
217  log::devel_guard guard(task_wrapper_tag);
218  flog_devel(info) << "In execute_user_task" << std::endl;
219  }
220 
221  // Unpack task arguments
222  // TODO: Can we deserialize directly into the user's parameters (i.e., do
223  // without finalize_handles)?
224  auto task_args = detail::tuple_get<param_tuple>(*task);
225 
226  bind_accessors_t bind_accessors(runtime, context, regions, task->futures);
227  bind_accessors.walk(task_args);
228 
229  if constexpr(std::is_same_v<RETURN, void>) {
230  apply(F, std::forward<param_tuple>(task_args));
231 
232  // FIXME: Refactor
233  // finalize_handles_t finalize_handles;
234  // finalize_handles.walk(task_args);
235  }
236  else {
237  RETURN result = apply(F, std::forward<param_tuple>(task_args));
238 
239  // FIXME: Refactor
240  // finalize_handles_t finalize_handles;
241  // finalize_handles.walk(task_args);
242 
243  return result;
244  } // if
245  } // execute_user_task
246 
247 }; // struct task_wrapper
248 
249 template<auto & F>
252  using RETURN = typename Traits::return_type;
253  using param_tuple = typename Traits::arguments_type;
254 
255  static constexpr auto LegionProcessor = task_processor_type_t::loc;
256 
257  static void execute(const Legion::Task * task,
258  const std::vector<Legion::PhysicalRegion> &,
259  Legion::Context,
260  Legion::Runtime *) {
261  // FIXME: Refactor
262  // {
263  // log::devel_guard guard(task_wrapper_tag);
264  // flog_devel(info) << "In execute_mpi_task" << std::endl;
265  // }
266 
267  // Unpack task arguments.
268  param_tuple * p;
269  flog_assert(task->arglen == sizeof p, "Bad Task::arglen");
270  std::memcpy(&p, task->args, sizeof p);
271  auto & mpi_task_args = *p;
272 
273  // FIXME: Refactor
274  // init_handles_t init_handles(runtime, context, regions, task->futures);
275  // init_handles.walk(mpi_task_args);
276 
277  // Set the MPI function and make the runtime active.
278  auto & c = run::context::instance();
279  c.set_mpi_task([&] { apply(F, std::move(mpi_task_args)); });
280 
281  // FIXME: Refactor
282  // finalize_handles_t finalize_handles;
283  // finalize_handles.walk(mpi_task_args);
284  }
285 };
286 
287 } // namespace exec::leg
288 } // namespace flecsi
Definition: task_wrapper.hh:104
task_processor_type_t
Definition: task_attributes.hh:60
Definition: task_wrapper.hh:200
void register_task()
Definition: task_wrapper.hh:144
static RETURN execute(const Legion::Task *task, const std::vector< Legion::PhysicalRegion > &regions, Legion::Context context, Legion::Runtime *runtime)
Definition: task_wrapper.hh:212
Definition: serialize.hh:206
decltype(auto) execute(ARGS &&... args)
Definition: execution.hh:382
#define flog_assert(test, message)
Definition: flog.hh:411
Definition: flog.hh:82
std::string type()
Definition: demangle.hh:44
void walk(TUPLE_TYPE &t)
Definition: tuple_walker.hh:75
Definition: function_traits.hh:29
Definition: launch.hh:95
Definition: bind_accessors.hh:51
const task_id_t task_id
Definition: field.hh:32
Definition: control.hh:31