coroutine.h
Go to the documentation of this file.
1 //
2 // Created by Eduard Valeyev on 10/31/22.
3 //
4 
5 #ifndef TTG_COROUTINE_H
6 #define TTG_COROUTINE_H
7 
8 #include "ttg/config.h"
9 
10 #ifdef TTG_HAVE_COROUTINE
11 #include TTG_CXX_COROUTINE_HEADER
12 
13 #include <algorithm>
14 #include <array>
15 
16 
17 namespace ttg {
18 
19  // import std coroutine API into ttg namespace
20 
23  template <typename Promise>
24  using coroutine_handle = TTG_CXX_COROUTINE_NAMESPACE::coroutine_handle<Promise>;
25 
27 
31 
33 
34  // fwd-declares
35 
36  struct resumable_task_state;
37 
38  template <std::size_t N>
39  struct resumable_task_events;
40 
42  struct event {
43  void finish() { finished_ = true; }
44 
46  bool finished() const { return finished_; }
47 
48  private:
49  std::atomic<bool> finished_ = false;
50  };
51 
53  struct resumable_task : public ttg::coroutine_handle<resumable_task_state> {
55 
58 
60 
62 
63  resumable_task(base_type base) : base_type(std::move(base)) {}
64 
65  base_type handle() { return *this; }
66 
68  inline bool ready() const;
69 
71  inline bool completed() const;
72 
74  inline ttg::span<event*> events();
75  };
76 
80  resumable_task_state() noexcept = default;
81  // these only live on coroutine frames so make noncopyable and nonmovable
83  resumable_task_state& operator=(const resumable_task_state&) = delete;
86 
87  constexpr static inline std::size_t MaxNumEvents = 20;
89 
92 
93  resumable_task get_return_object() { return resumable_task{handle_type::from_promise(*this)}; }
94 
96  suspend_never initial_suspend() noexcept { return {}; }
97 
100  completed_ = true;
101  return {};
102  }
103  void return_void() {}
105 
107 
110 
111  // these can be used to use optional storage provided by the runtime (e.g. part of the runtime's task data struct)
112  // N.B. the existing buffer must be passed to operator new via TLS
113  // void* operator new(std::size_t size)
114  // {
115  // return ::operator new(size);
116  // }
117 
118  // N.B. whether the external buffer was used by operator new must be passed via TLS
119  // void operator delete(void* ptr, std::size_t size)
120  // {
121  // ::operator delete(ptr, size);
122  // }
123 
125 
127  constexpr bool ready() const {
128  for (std::size_t e = 0; e != nevents_; ++e)
129  if (!events_storage_[e]->finished()) return false;
130  return true;
131  }
132 
134  constexpr bool completed() const { return completed_; }
135 
136  ttg::span<event*> events() { return ttg::span(events_storage_.data(), nevents_); }
137 
138  private:
139  std::array<event*, MaxNumEvents> events_storage_;
140  std::size_t nevents_;
141  bool completed_ = false;
142 
143  template <std::size_t N>
144  friend struct resumable_task_events;
145 
146  void reset_events() {
147  std::fill(events_storage_.begin(), events_storage_.begin() + nevents_, nullptr);
148  nevents_ = 0;
149  }
150 
151  template <std::size_t N>
152  void set_events(const std::array<event*, N> events) {
153  static_assert(N <= MaxNumEvents);
154  std::copy(events.begin(), events.end(), events_storage_.begin());
155  nevents_ = N;
156  }
157  };
158 
159  bool resumable_task::ready() const { return base_type::promise().ready(); }
160  bool resumable_task::completed() const { return base_type::promise().completed(); }
161  ttg::span<event*> resumable_task::events() { return base_type::promise().events(); }
162 
167  template <std::size_t N>
169  private:
170  template <std::size_t... I>
171  constexpr bool await_ready(std::index_sequence<I...>) const {
172  return (std::get<I>(events_)->finished() && ...);
173  }
174 
175  public:
176  template <typename... Events>
177  constexpr resumable_task_events(Events&&... events) : events_{(&events)...} {}
178 
181 
182  constexpr bool await_ready() const { return await_ready(std::make_index_sequence<N>{}); }
183 
185  pending_task_ = pending_task;
186  pending_task_.promise().set_events(events_);
187  }
188 
189  void await_resume() {
190  if (pending_task_) {
191  pending_task_.promise().reset_events();
192  pending_task_ = {};
193  }
194  }
195 
197 
198  private:
199  std::array<event*, N> events_;
201  }; // resumable_task_events
202 
203  // deduce the number of events properly
204  template <typename... Events>
205  resumable_task_events(Events&&...) -> resumable_task_events<sizeof...(Events)>;
206 
207  static_assert(resumable_task_events<0>{}.await_ready() == true);
208 
210 
212  // describe all types of coroutine tasks known to TTG
214 
215  // fwd declare all coro promise types that have not been declared yet
216  namespace device::detail {
217  struct device_task_promise_type;
218  } // namespace device::detail
219 
222  enum class TaskCoroutineID {
224  Invalid,
228  DeviceTask
229  };
230 
231 } // namespace ttg
232 
233 #endif // TTG_HAVE_COROUTINE
234 
235 #endif // TTG_COROUTINE_H
bool ready() const
Definition: coroutine.h:159
ttg::span< event * > events()
Definition: coroutine.h:161
bool completed() const
Definition: coroutine.h:160
resumable_task_events(Events &&...) -> resumable_task_events< sizeof...(Events)>
top-level TTG namespace contains runtime-neutral functionality
Definition: keymap.h:8
TTG_CXX_COROUTINE_NAMESPACE::suspend_always suspend_always
Definition: coroutine.h:21
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
TaskCoroutineID
Definition: coroutine.h:222
@ ResumableTask
-> ttg::resumable_task
@ Invalid
not a coroutine, i.e. a standard task function, -> void
@ DeviceTask
-> ttg::device::Task
represents a generic one-time event
Definition: coroutine.h:42
bool finished() const
Definition: coroutine.h:46
void finish()
Definition: coroutine.h:43
constexpr bool await_ready() const
Definition: coroutine.h:182
constexpr resumable_task_events(Events &&... events)
Definition: coroutine.h:177
void await_suspend(coroutine_handle< resumable_task_state > pending_task)
Definition: coroutine.h:184
constexpr bool completed() const
Definition: coroutine.h:134
resumable_task get_return_object()
Definition: coroutine.h:93
resumable_task_state() noexcept=default
constexpr bool ready() const
Definition: coroutine.h:127
suspend_always final_suspend() noexcept
Definition: coroutine.h:99
suspend_never initial_suspend() noexcept
Definition: coroutine.h:96
constexpr static std::size_t MaxNumEvents
Definition: coroutine.h:87
ttg::span< event * > events()
Definition: coroutine.h:136
coroutine_handle< resumable_task_state > handle_type
Definition: coroutine.h:88
task that can be resumed after some events occur
Definition: coroutine.h:53
resumable_task(base_type base)
Definition: coroutine.h:63
ttg::coroutine_handle< resumable_task_state > base_type
Definition: coroutine.h:54
base_type handle()
Definition: coroutine.h:65