task.h
Go to the documentation of this file.
1 #ifndef TTG_DEVICE_TASK_H
2 #define TTG_DEVICE_TASK_H
3 
4 #include <array>
5 #include <type_traits>
6 #include <span>
7 
8 #include "ttg/fwd.h"
9 #include "ttg/impl_selector.h"
10 #include "ttg/ptr.h"
11 
12 #ifdef TTG_HAVE_COROUTINE
13 
14 namespace ttg::device {
15 
16  namespace detail {
17  template <typename... Ts>
18  struct to_device_t {
19  std::tuple<std::add_lvalue_reference_t<Ts>...> ties;
20  };
21  } // namespace detail
22 
30  template <typename... Args>
31  [[nodiscard]]
32  inline auto select(Args &&...args) {
33  return detail::to_device_t<std::remove_reference_t<Args>...>{std::tie(std::forward<Args>(args)...)};
34  }
35 
36  namespace detail {
37 
38  enum ttg_device_coro_state {
39  TTG_DEVICE_CORO_STATE_NONE,
40  TTG_DEVICE_CORO_INIT,
41  TTG_DEVICE_CORO_WAIT_TRANSFER,
42  TTG_DEVICE_CORO_WAIT_KERNEL,
43  TTG_DEVICE_CORO_SENDOUT,
44  TTG_DEVICE_CORO_COMPLETE
45  };
46 
47  template <typename... Ts>
48  struct wait_kernel_t {
49  std::tuple<Ts &...> ties;
50 
51  /* always suspend */
52  constexpr bool await_ready() const noexcept { return false; }
53 
54  /* always suspend */
55  template <typename Promise>
56  constexpr void await_suspend(ttg::coroutine_handle<Promise>) const noexcept {}
57 
58  void await_resume() noexcept {
59  if constexpr (sizeof...(Ts) > 0) {
60  /* hook to allow the backend to handle the data after pushout */
62  }
63  }
64  };
65  } // namespace detail
66 
72  template <typename... Buffers>
73  [[nodiscard]]
74  inline auto wait(Buffers &&...args) {
75  static_assert(
76  ((ttg::meta::is_buffer_v<std::decay_t<Buffers>> || ttg::meta::is_devicescratch_v<std::decay_t<Buffers>>) &&
77  ...),
78  "Only ttg::Buffer and ttg::devicescratch can be waited on!");
79  return detail::wait_kernel_t<std::remove_reference_t<Buffers>...>{std::tie(std::forward<Buffers>(args)...)};
80  }
81 
82  /******************************
83  * Send/Broadcast handling
84  * We pass the value returned by the backend's copy handler into a coroutine
85  * and execute the first part (prepare), before suspending it.
86  * The second part (send/broadcast) is executed after the task completed.
87  ******************************/
88 
89  namespace detail {
90  struct send_coro_promise_type;
91 
92  using send_coro_handle_type = ttg::coroutine_handle<send_coro_promise_type>;
93 
95  struct send_coro_state : public send_coro_handle_type {
96  using base_type = send_coro_handle_type;
97 
100 
101  using promise_type = send_coro_promise_type;
102 
104 
105  send_coro_state(base_type base) : base_type(std::move(base)) {}
106 
107  base_type &handle() { return *this; }
108 
110  inline bool ready() { return true; }
111 
113  inline bool completed();
114  };
115 
117  struct send_coro_promise_type {
118  /* do not suspend the coroutine on first invocation, we want to run
119  * the coroutine immediately and suspend only once.
120  */
121  ttg::suspend_never initial_suspend() { return {}; }
122 
123  /* we don't suspend the coroutine at the end.
124  * it can be destroyed once the send/broadcast is done
125  */
126  ttg::suspend_never final_suspend() noexcept { return {}; }
127 
128  send_coro_state get_return_object() { return send_coro_state{send_coro_handle_type::from_promise(*this)}; }
129 
130  /* the send coros only have an empty co_await */
131  ttg::suspend_always await_transform(ttg::Void) { return {}; }
132 
133  void unhandled_exception() {
134  std::cerr << "Send coroutine caught an unhandled exception!" << std::endl;
135  throw; // fwd
136  }
137 
138  void return_void() {}
139  };
140 
141  template <typename Key, typename Value, ttg::Runtime Runtime = ttg::ttg_runtime>
142  inline send_coro_state send_coro(const Key &key, Value &&value, ttg::Out<Key, std::decay_t<Value>> &t,
144  ttg::detail::value_copy_handler<Runtime> copy_handler = std::move(ch); // destroyed at the end of the coro
145  Key k = key;
146  t.prepare_send(k, std::forward<Value>(value));
147  co_await ttg::Void{}; // we'll come back once the task is done
148  t.send(k, std::forward<Value>(value));
149  };
150 
151  template <typename Value, ttg::Runtime Runtime = ttg::ttg_runtime>
152  inline send_coro_state sendv_coro(Value &&value, ttg::Out<void, std::decay_t<Value>> &t,
154  ttg::detail::value_copy_handler<Runtime> copy_handler = std::move(ch); // destroyed at the end of the coro
155  t.prepare_send(std::forward<Value>(value));
156  co_await ttg::Void{}; // we'll come back once the task is done
157  t.sendv(std::forward<Value>(value));
158  };
159 
160  template <typename Key, ttg::Runtime Runtime = ttg::ttg_runtime>
161  inline send_coro_state sendk_coro(const Key &key, ttg::Out<Key, void> &t) {
162  // no need to prepare the send but we have to suspend once
163  Key k = key;
164  co_await ttg::Void{}; // we'll come back once the task is done
165  t.sendk(k);
166  };
167 
168  template <ttg::Runtime Runtime = ttg::ttg_runtime>
169  inline send_coro_state send_coro(ttg::Out<void, void> &t) {
170  // no need to prepare the send but we have to suspend once
171  co_await ttg::Void{}; // we'll come back once the task is done
172  t.send();
173  };
174 
175  struct send_t {
176  send_coro_state coro;
177  };
178  } // namespace detail
179 
180  template <size_t i, typename keyT, typename valueT, typename... out_keysT, typename... out_valuesT,
182  inline detail::send_t send(const keyT &key, valueT &&value, std::tuple<ttg::Out<out_keysT, out_valuesT>...> &t) {
184  return detail::send_t{
185  detail::send_coro(key, copy_handler(std::forward<valueT>(value)), std::get<i>(t), copy_handler)};
186  }
187 
188  template <size_t i, typename valueT, typename... out_keysT, typename... out_valuesT,
190  inline detail::send_t sendv(valueT &&value, std::tuple<ttg::Out<out_keysT, out_valuesT>...> &t) {
192  return detail::send_t{detail::sendv_coro(copy_handler(std::forward<valueT>(value)), std::get<i>(t), copy_handler)};
193  }
194 
195  template <size_t i, typename Key, typename... out_keysT, typename... out_valuesT,
197  inline detail::send_t sendk(const Key &key, std::tuple<ttg::Out<out_keysT, out_valuesT>...> &t) {
198  return detail::send_t{detail::sendk_coro(key, std::get<i>(t))};
199  }
200 
201  // clang-format off
206  // clang-format on
207  template <typename keyT, typename valueT, ttg::Runtime Runtime = ttg::ttg_runtime>
208  inline detail::send_t send(size_t i, const keyT &key, valueT &&value) {
210  auto *terminal_ptr = ttg::detail::get_out_terminal<keyT, valueT>(i, "ttg::device::send(i, key, value)");
211  return detail::send_t{detail::send_coro(key, copy_handler(std::forward<valueT>(value)), *terminal_ptr, copy_handler)};
212  }
213 
214  // clang-format off
220  // clang-format on
221  template <size_t i, typename keyT, typename valueT>
222  inline auto send(const keyT &key, valueT &&value) {
223  return ttg::device::send(i, key, std::forward<valueT>(value));
224  }
225 
226 
227  template <typename valueT, ttg::Runtime Runtime = ttg::ttg_runtime>
228  inline detail::send_t sendv(std::size_t i, valueT &&value) {
229  auto *terminal_ptr = ttg::detail::get_out_terminal<void, valueT>(i, "ttg::device::send(i, key, value)");
231  return detail::send_t{detail::sendv_coro(copy_handler(std::forward<valueT>(value)), *terminal_ptr, copy_handler)};
232  }
233 
234  template <typename Key, ttg::Runtime Runtime = ttg::ttg_runtime>
235  inline detail::send_t sendk(std::size_t i, const Key& key) {
236  auto *terminal_ptr = ttg::detail::get_out_terminal<Key, void>(i, "ttg::device::send(i, key, value)");
237  return detail::send_t{detail::sendk_coro(key, *terminal_ptr)};
238  }
239 
240  template <ttg::Runtime Runtime = ttg::ttg_runtime>
241  inline detail::send_t send(std::size_t i) {
242  auto *terminal_ptr = ttg::detail::get_out_terminal<void, void>(i, "ttg::device::send(i, key, value)");
243  return detail::send_t{detail::send_coro(*terminal_ptr)};
244  }
245 
246 
247  template <std::size_t i, typename valueT, typename... out_keysT, typename... out_valuesT,
249  inline detail::send_t sendv(valueT &&value) {
250  return sendv(i, std::forward<valueT>(value));
251  }
252 
253  template <size_t i, typename Key, ttg::Runtime Runtime = ttg::ttg_runtime>
254  inline detail::send_t sendk(const Key& key) {
255  return sendk(i, key);
256  }
257 
258  template <size_t i, ttg::Runtime Runtime = ttg::ttg_runtime>
259  inline detail::send_t sendk() {
260  return send(i);
261  }
262 
263  namespace detail {
264 
265  template<typename T, typename Enabler = void>
266  struct broadcast_keylist_trait {
267  using type = T;
268  };
269 
270  /* overload for iterable types that extracts the type of the first element */
271  template<typename T>
272  struct broadcast_keylist_trait<T, std::enable_if_t<ttg::meta::is_iterable_v<T>>> {
273  using key_type = decltype(*std::begin(std::get<0>(std::declval<T>())));
274  };
275 
276  template <size_t KeyId, size_t I, size_t... Is, typename... RangesT, typename valueT,
277  typename... out_keysT, typename... out_valuesT>
278  inline void prepare_broadcast(const std::tuple<RangesT...> &keylists, valueT &&value,
279  std::tuple<ttg::Out<out_keysT, out_valuesT>...> &t) {
280  std::get<I>(t).prepare_send(std::get<KeyId>(keylists), std::forward<valueT>(value));
281  if constexpr (sizeof...(Is) > 0) {
282  prepare_broadcast<KeyId+1, Is...>(keylists, std::forward<valueT>(value), t);
283  }
284  }
285 
286  template <size_t KeyId, size_t I, size_t... Is, typename... RangesT, typename valueT,
287  typename... out_keysT, typename... out_valuesT>
288  inline void prepare_broadcast(const std::tuple<RangesT...> &keylists, valueT &&value) {
289  using key_t = typename broadcast_keylist_trait<
290  std::tuple_element_t<KeyId, std::tuple<std::remove_reference_t<RangesT>...>>
291  >::key_type;
292  auto *terminal_ptr = ttg::detail::get_out_terminal<key_t, valueT>(I, "ttg::device::broadcast(keylists, value)");
293  terminal_ptr->prepare_send(std::get<KeyId>(keylists), value);
294  if constexpr (sizeof...(Is) > 0) {
295  prepare_broadcast<KeyId+1, Is...>(keylists, std::forward<valueT>(value));
296  }
297  }
298 
299  template <size_t KeyId, size_t I, size_t... Is, typename... RangesT, typename valueT,
300  typename... out_keysT, typename... out_valuesT>
301  inline void broadcast(const std::tuple<RangesT...> &keylists, valueT &&value,
302  std::tuple<ttg::Out<out_keysT, out_valuesT>...> &t) {
303  std::get<I>(t).broadcast(std::get<KeyId>(keylists), std::forward<valueT>(value));
304  if constexpr (sizeof...(Is) > 0) {
305  detail::broadcast<KeyId+1, Is...>(keylists, std::forward<valueT>(value), t);
306  }
307  }
308 
309  template <size_t KeyId, size_t I, size_t... Is, typename... RangesT, typename valueT,
310  typename... out_keysT, typename... out_valuesT>
311  inline void broadcast(const std::tuple<RangesT...> &keylists, valueT &&value) {
312  using key_t = typename broadcast_keylist_trait<
313  std::tuple_element_t<KeyId, std::tuple<std::remove_reference_t<RangesT>...>>
314  >::key_type;
315  auto *terminal_ptr = ttg::detail::get_out_terminal<key_t, valueT>(I, "ttg::device::broadcast(keylists, value)");
316  terminal_ptr->broadcast(std::get<KeyId>(keylists), value);
317  if constexpr (sizeof...(Is) > 0) {
318  ttg::device::detail::broadcast<KeyId+1, Is...>(keylists, std::forward<valueT>(value));
319  }
320  }
321 
322  /* overload with explicit terminals */
323  template <size_t I, size_t... Is, typename RangesT, typename valueT,
324  typename... out_keysT, typename... out_valuesT,
326  inline send_coro_state
327  broadcast_coro(RangesT &&keylists, valueT &&value,
328  std::tuple<ttg::Out<out_keysT, out_valuesT>...> &t,
330  ttg::detail::value_copy_handler<Runtime> copy_handler = std::move(ch); // destroyed at the end of the coro
331  RangesT kl = std::forward<RangesT>(keylists); // capture the keylist(s)
332  if constexpr (ttg::meta::is_tuple_v<RangesT>) {
333  // treat as tuple
334  prepare_broadcast<0, I, Is...>(kl, std::forward<std::decay_t<decltype(value)>>(value), t);
335  co_await ttg::Void{}; // we'll come back once the task is done
336  ttg::device::detail::broadcast<0, I, Is...>(kl, std::forward<std::decay_t<decltype(value)>>(value), t);
337  } else if constexpr (!ttg::meta::is_tuple_v<RangesT>) {
338  // create a tie to the captured keylist
339  prepare_broadcast<0, I, Is...>(std::tie(kl), std::forward<std::decay_t<decltype(value)>>(value), t);
340  co_await ttg::Void{}; // we'll come back once the task is done
341  ttg::device::detail::broadcast<0, I, Is...>(std::tie(kl), std::forward<std::decay_t<decltype(value)>>(value), t);
342  }
343  }
344 
345  /* overload with implicit terminals */
346  template <size_t I, size_t... Is, typename RangesT, typename valueT,
348  inline send_coro_state
349  broadcast_coro(RangesT &&keylists, valueT &&value,
351  ttg::detail::value_copy_handler<Runtime> copy_handler = std::move(ch); // destroyed at the end of the coro
352  RangesT kl = std::forward<RangesT>(keylists); // capture the keylist(s)
353  if constexpr (ttg::meta::is_tuple_v<RangesT>) {
354  // treat as tuple
355  static_assert(sizeof...(Is)+1 == std::tuple_size_v<RangesT>,
356  "Size of keylist tuple must match the number of output terminals");
357  prepare_broadcast<0, I, Is...>(kl, std::forward<std::decay_t<decltype(value)>>(value));
358  co_await ttg::Void{}; // we'll come back once the task is done
359  ttg::device::detail::broadcast<0, I, Is...>(kl, std::forward<std::decay_t<decltype(value)>>(value));
360  } else if constexpr (!ttg::meta::is_tuple_v<RangesT>) {
361  // create a tie to the captured keylist
362  prepare_broadcast<0, I, Is...>(std::tie(kl), std::forward<std::decay_t<decltype(value)>>(value));
363  co_await ttg::Void{}; // we'll come back once the task is done
364  ttg::device::detail::broadcast<0, I, Is...>(std::tie(kl), std::forward<std::decay_t<decltype(value)>>(value));
365  }
366  }
367  } // namespace detail
368 
369  /* overload with explicit terminals and keylist passed by const reference */
370  template <size_t I, size_t... Is, typename rangeT, typename valueT, typename... out_keysT, typename... out_valuesT,
372  [[nodiscard]]
373  inline detail::send_t broadcast(rangeT &&keylist,
374  valueT &&value,
375  std::tuple<ttg::Out<out_keysT, out_valuesT>...> &t) {
377  return detail::send_t{
378  detail::broadcast_coro<I, Is...>(std::forward<rangeT>(keylist),
379  copy_handler(std::forward<valueT>(value)),
380  t, std::move(copy_handler))};
381  }
382 
383  /* overload with implicit terminals and keylist passed by const reference */
384  template <size_t i, typename rangeT, typename valueT,
386  inline detail::send_t broadcast(rangeT &&keylist, valueT &&value) {
388  return detail::send_t{broadcast_coro<i>(std::tie(keylist), copy_handler(std::forward<valueT>(value)),
389  std::move(copy_handler))};
390  }
391 
392  template<typename... Args, ttg::Runtime Runtime = ttg::ttg_runtime>
393  [[nodiscard]]
394  std::vector<device::detail::send_t> forward(Args&&... args) {
395  // TODO: check the cost of this!
396  return std::vector{std::forward<Args>(args)...};
397  }
398 
399  /*******************************************
400  * Device task promise and coroutine handle
401  *******************************************/
402 
403  namespace detail {
404  // fwd-decl
405  struct device_task_promise_type;
406  // base type for ttg::device::Task
407  using device_task_handle_type = ttg::coroutine_handle<device_task_promise_type>;
408  } // namespace detail
409 
411 
418  struct Task : public detail::device_task_handle_type {
419  using base_type = detail::device_task_handle_type;
420 
423 
424  using promise_type = detail::device_task_promise_type;
425 
427 
428  Task(base_type base) : base_type(std::move(base)) {}
429 
430  base_type& handle() { return *this; }
431 
433  inline bool ready() {
434  return true;
435  }
436 
438  inline bool completed();
439  };
440 
441  namespace detail {
442 
443  /* The promise type that stores the views provided by the
444  * application task coroutine on the first co_yield. It subsequently
445  * tracks the state of the task when it moves from waiting for transfers
446  * to waiting for the submitted kernel to complete. */
447  struct device_task_promise_type {
448 
449  /* do not suspend the coroutine on first invocation, we want to run
450  * the coroutine immediately and suspend when we get the device transfers.
451  */
452  ttg::suspend_never initial_suspend() {
453  m_state = ttg::device::detail::TTG_DEVICE_CORO_INIT;
454  return {};
455  }
456 
457  /* suspend the coroutine at the end of the execution
458  * so we can access the promise.
459  * TODO: necessary? maybe we can save one suspend here
460  */
461  ttg::suspend_always final_suspend() noexcept {
462  m_state = ttg::device::detail::TTG_DEVICE_CORO_COMPLETE;
463  return {};
464  }
465 
466  /* Allow co_await on a tuple */
467  template<typename... Views>
468  ttg::suspend_always await_transform(std::tuple<Views&...> &views) {
469  return yield_value(views);
470  }
471 
472  template<typename... Ts>
473  ttg::suspend_always await_transform(detail::to_device_t<Ts...>&& a) {
474  bool need_transfer = !(TTG_IMPL_NS::register_device_memory(a.ties));
475  /* TODO: are we allowed to not suspend here and launch the kernel directly? */
476  m_state = ttg::device::detail::TTG_DEVICE_CORO_WAIT_TRANSFER;
477  return {};
478  }
479 
480  template<typename... Ts>
481  auto await_transform(detail::wait_kernel_t<Ts...>&& a) {
482  //std::cout << "yield_value: wait_kernel_t" << std::endl;
483  if constexpr (sizeof...(Ts) > 0) {
485  }
486  m_state = ttg::device::detail::TTG_DEVICE_CORO_WAIT_KERNEL;
487  return a;
488  }
489 
490  ttg::suspend_always await_transform(std::vector<device::detail::send_t>&& v) {
491  m_sends = std::forward<std::vector<device::detail::send_t>>(v);
492  m_state = ttg::device::detail::TTG_DEVICE_CORO_SENDOUT;
493  return {};
494  }
495 
496  ttg::suspend_always await_transform(device::detail::send_t&& v) {
497  m_sends.clear();
498  m_sends.push_back(std::forward<device::detail::send_t>(v));
499  m_state = ttg::device::detail::TTG_DEVICE_CORO_SENDOUT;
500  return {};
501  }
502 
503  void return_void() {
504  m_state = ttg::device::detail::TTG_DEVICE_CORO_COMPLETE;
505  }
506 
507  bool complete() const {
508  return m_state == ttg::device::detail::TTG_DEVICE_CORO_COMPLETE;
509  }
510 
511  ttg::device::Task get_return_object() { return {detail::device_task_handle_type::from_promise(*this)}; }
512 
513  void unhandled_exception() {
514  std::cerr << "Task coroutine caught an unhandled exception!" << std::endl;
515  throw; // fwd
516  }
517 
518  //using iterator = std::vector<device_obj_view>::iterator;
519 
520  /* execute all pending send and broadcast operations */
521  void do_sends() {
522  for (auto& send : m_sends) {
523  send.coro();
524  }
525  m_sends.clear();
526  }
527 
528  auto state() {
529  return m_state;
530  }
531 
532  private:
533  std::vector<device::detail::send_t> m_sends;
534  ttg_device_coro_state m_state = ttg::device::detail::TTG_DEVICE_CORO_STATE_NONE;
535 
536  };
537 
538  } // namespace detail
539 
540  bool Task::completed() { return base_type::promise().state() == ttg::device::detail::TTG_DEVICE_CORO_COMPLETE; }
541 
542  struct device_wait_kernel
543  { };
544 
545 
546  /* NOTE: below is preliminary for reductions on the device, which is not available yet */
547 #if 0
548  /**************************
549  * Device reduction coros *
550  **************************/
551 
552  struct device_reducer_promise_type;
553 
554  using device_reducer_handle_type = ttg::coroutine_handle<device_reducer_promise_type>;
555 
557  struct device_reducer : public device_reducer_handle_type {
558  using base_type = device_reducer_handle_type;
559 
562 
563  using promise_type = device_reducer_promise_type;
564 
566 
567  device_reducer(base_type base) : base_type(std::move(base)) {}
568 
569  base_type& handle() { return *this; }
570 
572  inline bool ready() {
573  return true;
574  }
575 
577  inline bool completed();
578  };
579 
580 
581  /* The promise type that stores the views provided by the
582  * application task coroutine on the first co_yield. It subsequently
583  * tracks the state of the task when it moves from waiting for transfers
584  * to waiting for the submitted kernel to complete. */
585  struct device_reducer_promise_type {
586 
587  /* do not suspend the coroutine on first invocation, we want to run
588  * the coroutine immediately and suspend when we get the device transfers.
589  */
590  ttg::suspend_never initial_suspend() {
591  m_state = ttg::device::detail::TTG_DEVICE_CORO_INIT;
592  return {};
593  }
594 
595  /* suspend the coroutine at the end of the execution
596  * so we can access the promise.
597  * TODO: necessary? maybe we can save one suspend here
598  */
599  ttg::suspend_always final_suspend() noexcept {
600  m_state = ttg::device::detail::TTG_DEVICE_CORO_COMPLETE;
601  return {};
602  }
603 
604  template<typename... Ts>
605  ttg::suspend_always await_transform(detail::to_device_t<Ts...>&& a) {
606  bool need_transfer = !(TTG_IMPL_NS::register_device_memory(a.ties));
607  /* TODO: are we allowed to not suspend here and launch the kernel directly? */
608  m_state = ttg::device::detail::TTG_DEVICE_CORO_WAIT_TRANSFER;
609  return {};
610  }
611 
612  void return_void() {
613  m_state = ttg::device::detail::TTG_DEVICE_CORO_COMPLETE;
614  }
615 
616  bool complete() const {
617  return m_state == ttg::device::detail::TTG_DEVICE_CORO_COMPLETE;
618  }
619 
620  device_reducer get_return_object() { return device_reducer{device_reducer_handle_type::from_promise(*this)}; }
621 
622  void unhandled_exception() { }
623 
624  auto state() {
625  return m_state;
626  }
627 
628 
629  private:
630  ttg::device::detail::ttg_device_coro_state m_state = ttg::device::detail::TTG_DEVICE_CORO_STATE_NONE;
631 
632  };
633 
634  bool device_reducer::completed() { return base_type::promise().state() == ttg::device::detail::TTG_DEVICE_CORO_COMPLETE; }
635 #endif // 0
636 
637 } // namespace ttg::device
638 
639 #endif // TTG_HAVE_COROUTINE
640 
641 #endif // TTG_DEVICE_TASK_H
std::enable_if_t< meta::is_all_void_v< Key, Value >, void > send()
Definition: terminal.h:514
std::enable_if_t<!meta::is_void_v< Key > &&meta::is_void_v< Value >, void > sendk(const Key &key)
Definition: terminal.h:475
A complete version of void.
Definition: void.h:11
void broadcast(const std::tuple< RangesT... > &keylists, valueT &&value, std::tuple< ttg::Out< out_keysT, out_valuesT >... > &t)
Definition: func.h:347
constexpr bool is_devicescratch_v
Definition: meta.h:340
constexpr bool is_buffer_v
Definition: meta.h:325
void mark_device_out(std::tuple< Buffer &... > &b)
bool register_device_memory(std::tuple< Views &... > &views)
void post_device_out(std::tuple< Buffer &... > &b)
TTG_CXX_COROUTINE_NAMESPACE::suspend_always suspend_always
Definition: coroutine.h:21
void send(const keyT &key, valueT &&value, ttg::Out< keyT, valueT > &t)
Sends a task id and a value to the given output terminal.
Definition: func.h:158
constexpr const ttg::Runtime ttg_runtime
Definition: import.h:20
Runtime
Definition: runtimes.h:15
TTG_CXX_COROUTINE_NAMESPACE::suspend_never suspend_never
Definition: coroutine.h:22
TTG_CXX_COROUTINE_NAMESPACE::coroutine_handle< Promise > coroutine_handle
Definition: coroutine.h:24
void sendk(const keyT &key, ttg::Out< keyT, void > &t)
Sends a task id (without an accompanying value) to the given output terminal.
Definition: func.h:169
void sendv(valueT &&value, ttg::Out< void, valueT > &t)
Sends a value (without an accompanying task id) to the given output terminal.
Definition: func.h:179
void broadcast(const rangeT &keylist, valueT &&value, std::tuple< ttg::Out< out_keysT, out_valuesT >... > &t)
Definition: func.h:414