Uni20
Asynchronous tensor and runtime library.
Loading...
Searching...
No Matches
awaiters.hpp
Go to the documentation of this file.
1
3
4#pragma once
5
6#include "async.hpp"
7#include "buffers.hpp"
8
9#include <array>
10#include <concepts>
11#include <coroutine>
12#include <functional>
13#include <memory>
14#include <optional>
15#include <tuple>
16#include <type_traits>
17#include <utility>
18
19namespace uni20::async
20{
21
26template <typename T> struct ValueAwaiter
27{
28 using value_type = T;
29
31
33 [[nodiscard]] bool await_ready() const noexcept { return true; }
34
36 void await_suspend(BasicTask) const noexcept {}
37
39 [[nodiscard]] T const& await_resume() const& noexcept { return value; }
40
42 [[nodiscard]] T await_resume() && noexcept { return std::move(value); }
43
45 void release() const noexcept {}
46};
47
48namespace detail
49{
50template <typename T> using member_read_awaiter_t = std::remove_cvref_t<decltype(std::declval<T>().read())>;
51
52template <typename T>
53concept AsyncReadProvider = requires(T&& value) { std::forward<T>(value).read(); } &&
54 TaskAwaitable<member_read_awaiter_t<T>> && requires(member_read_awaiter_t<T>& awaiter) {
55 typename member_read_awaiter_t<T>::value_type;
56 { awaiter.await_ready() } -> std::convertible_to<bool>;
57 awaiter.await_resume();
58 };
59
60template <typename Result>
61using all_result_element_t =
62 std::conditional_t<std::is_lvalue_reference_v<Result>, Result, std::remove_cvref_t<Result>>;
63
64template <typename Awaiter>
65using all_result_t = all_result_element_t<decltype(std::declval<Awaiter>().await_resume())>;
66} // namespace detail
67
69template <typename T>
70 requires(!detail::AsyncReadProvider<T>)
72{
73 return ValueAwaiter<std::remove_cvref_t<T>>{std::forward<T>(value)};
74}
75
77template <typename T>
78 requires detail::AsyncReadProvider<T>
79[[nodiscard]] decltype(auto) read(T&& value)
80{
81 return std::forward<T>(value).read();
82}
83
90template <TaskFactoryChildAwaitable... Aw>
91 requires((!std::is_void_v<decltype(std::declval<Aw>().await_resume())> && ...))
92struct AllAwaiter //: public AsyncAwaiter
93{
94 std::tuple<Aw...> bufs_;
95 std::array<bool, sizeof...(Aw)> ready_{};
96 int pending_ = sizeof...(Aw);
97
100 [[nodiscard]] bool await_ready() noexcept { return await_ready_impl(std::make_index_sequence<sizeof...(Aw)>{}); }
101
104 [[nodiscard]] int num_awaiters() const noexcept { return pending_; }
105
106#if UNI20_DEBUG_DAG
110 template <typename F> void debug_each_dependency(F&& visit) const
111 {
112 std::apply([&](auto const&... awaiters) { (debug_visit_dependency(awaiters, visit), ...); }, bufs_);
113 }
114#endif
115
119 void await_suspend(TaskFactory f) noexcept
120 {
121 await_suspend_impl(std::move(f), std::make_index_sequence<sizeof...(Aw)>{});
122 }
123
129 [[nodiscard]] auto await_resume()
130 {
131 return await_resume_impl(std::make_index_sequence<sizeof...(Aw)>{});
132 }
133
134 private:
135#if UNI20_DEBUG_DAG
136 template <typename A, typename F> static void debug_visit_dependency(A const& awaiter, F& visit)
137 {
138 if constexpr (requires {
139 awaiter.node();
140 awaiter.debug_task_role();
141 })
142 {
143 visit(awaiter.node(), awaiter.debug_task_role());
144 }
145 }
146#endif
147
148 template <std::size_t... I> bool await_ready_impl(std::index_sequence<I...>) noexcept
149 {
150 pending_ = 0;
151 ((ready_[I] = std::get<I>(bufs_).await_ready(), pending_ += ready_[I] ? 0 : 1), ...);
152 return pending_ == 0;
153 }
154
155 template <std::size_t... I> void await_suspend_impl(TaskFactory f, std::index_sequence<I...>) noexcept
156 {
157 ((ready_[I] ? void() : std::get<I>(bufs_).await_suspend(f.take_next())), ...);
158 }
159
160 template <std::size_t I> decltype(auto) await_resume_child()
161 {
162 using awaiter_type = std::tuple_element_t<I, std::tuple<Aw...>>;
163 return std::forward<awaiter_type>(std::get<I>(bufs_)).await_resume();
164 }
165
166 template <std::size_t... I> auto await_resume_impl(std::index_sequence<I...>)
167 {
168 using result_type = std::tuple<detail::all_result_t<Aw>...>;
169 return result_type{this->template await_resume_child<I>()...};
170 }
171};
172
173namespace detail
174{
175template <typename T> struct MapToRefOrValue
176{
177 static_assert(std::is_trivially_move_constructible_v<T>,
178 "Prvalue passed to all(...) must be trivially move constructible");
179 using type = T;
180};
181
182template <typename T> struct MapToRefOrValue<T&&>
183{
184 static_assert(std::is_trivially_move_constructible_v<T>,
185 "Rvalue passed to all(...) must be trivially move constructible");
186 using type = T;
187};
188
189template <typename T> struct MapToRefOrValue<T&>
190{
191 using type = T&;
192};
193} // namespace detail
194
198template <TaskFactoryChildAwaitable... Aw>
199 requires((!std::is_void_v<decltype(std::declval<Aw>().await_resume())> && ...))
200auto all(Aw&&... aw) noexcept
201{
203 std::tuple<typename detail::MapToRefOrValue<Aw>::type...>(std::forward<Aw>(aw)...));
204}
205
206//------------------------------------------------------------------------------
207// Non-blocking await: try_await
208//------------------------------------------------------------------------------
209
211template <typename T>
212concept HasMemberCoAwait = requires(T t) { t.operator co_await(); };
213
216template <typename T>
217concept HasFreeCoAwait = (!HasMemberCoAwait<T> && requires(T t) { operator co_await(t); });
218
225template <typename U> decltype(auto) get_awaiter(U&& u) noexcept
226{
227 if constexpr (HasMemberCoAwait<U>)
228 {
229 return std::forward<U>(u).operator co_await();
230 }
231 else if constexpr (HasFreeCoAwait<U>)
232 {
233 return operator co_await(std::forward<U>(u));
234 }
235 else
236 {
237 return std::forward<U>(u);
238 }
239}
240
243template <typename Awt> struct TryAwaiter
244{
245 using Awaiter = std::remove_reference_t<Awt>;
246 using StoredAwaiter = std::conditional_t<std::is_lvalue_reference_v<Awt>, Awaiter*, Awt>;
247
249
250 constexpr explicit TryAwaiter(Awt aw) noexcept : awaiter_(store(std::forward<Awt>(aw))) {}
251
252 [[nodiscard]] bool await_ready() const noexcept { return true; }
253
254 void await_suspend(BasicTask t) noexcept { access().await_suspend(std::move(t)); }
255
256 [[nodiscard]] auto await_resume()
257 {
258 auto& inner = access();
259 using ResumeAwaiter = std::conditional_t<std::is_lvalue_reference_v<Awt>, Awaiter&, Awaiter&&>;
260 using R = decltype(std::declval<ResumeAwaiter>().await_resume());
261 using StoreT =
262 std::conditional_t<std::is_lvalue_reference_v<R>, std::reference_wrapper<std::remove_reference_t<R>>,
263 std::remove_cvref_t<R>>;
264
265 if (!inner.await_ready())
266 {
267 return std::optional<StoreT>{};
268 }
269
270 if constexpr (std::is_lvalue_reference_v<R>)
271 {
272 return std::optional<StoreT>{std::ref(resume_inner(inner))};
273 }
274 else
275 {
276 return std::optional<StoreT>{resume_inner(inner)};
277 }
278 }
279
280 private:
281 static constexpr StoredAwaiter store(Awt aw) noexcept
282 {
283 if constexpr (std::is_lvalue_reference_v<Awt>)
284 {
285 return std::addressof(aw);
286 }
287 else
288 {
289 return std::move(aw);
290 }
291 }
292
293 [[nodiscard]] constexpr Awaiter& access() noexcept
294 {
295 if constexpr (std::is_lvalue_reference_v<Awt>)
296 {
297 return *awaiter_;
298 }
299 else
300 {
301 return awaiter_;
302 }
303 }
304
305 [[nodiscard]] constexpr Awaiter const& access() const noexcept
306 {
307 if constexpr (std::is_lvalue_reference_v<Awt>)
308 {
309 return *awaiter_;
310 }
311 else
312 {
313 return awaiter_;
314 }
315 }
316
317 [[nodiscard]] static decltype(auto) resume_inner(Awaiter& inner)
318 {
319 if constexpr (std::is_lvalue_reference_v<Awt>)
320 {
321 return inner.await_resume();
322 }
323 else
324 {
325 return std::move(inner).await_resume();
326 }
327 }
328};
329
349template <typename Aw> constexpr auto try_await(Aw& aw) noexcept { return TryAwaiter<Aw&>{aw}; }
350
355template <typename Aw> constexpr auto try_await(Aw&& aw) noexcept
356{
357 static_assert(std::is_trivially_move_constructible_v<Aw>, "try_await(x) on prvalues requires T to be safely movable");
358 return TryAwaiter<Aw>{std::move(aw)};
359}
360
361namespace detail
362{
379template <typename T, typename Value> class WriteToAwaiter {
380 public:
381 WriteToAwaiter(WriteBuffer<T>&& buffer, Value&& value)
382 : buffer_(std::move(buffer)), value_(std::forward<Value>(value))
383 {
384 static_assert(std::constructible_from<T, Value&&>, "Value must be constructible as T for WriteBuffer<T>");
385 }
386
387 [[nodiscard]] bool await_ready() const noexcept { return buffer_.await_ready(); }
388
389 auto await_suspend(BasicTask&& t) noexcept { return buffer_.await_suspend(std::move(t)); }
390
391 void await_resume() { buffer_.emplace_assert(std::move(value_)); }
392
393 private:
394 WriteBuffer<T> buffer_;
395 std::remove_reference_t<Value> value_;
396};
397} // namespace detail
398
409template <typename T, typename Value> auto write_to(WriteBuffer<T>&& buffer, Value value)
410{
411 return detail::WriteToAwaiter<T, Value>(std::move(buffer), std::move(value));
412}
413
414} // namespace uni20::async
Awaitable capabilities for shared reads and exclusive mutable access to Async<T>.
Move-only promise-neutral ownership claim for a Uni20 coroutine.
Definition async_task.hpp:103
Factory for producing multiple BasicTask ownership claims for one coroutine.
Definition async_task_promise.hpp:766
Exclusive mutable-access buffer for one Async<T> epoch.
Definition buffers.hpp:859
Detects if a free operator co_await(t) exists.
Definition awaiters.hpp:217
Detects if T has a member operator co_await().
Definition awaiters.hpp:212
locale_t value
Definition data_table.cpp:49
Definition qnum.hpp:403
This represents the reverse-mode adjoint for functions of complex variables, assuming a real-valued s...
Definition async/async.hpp:26
auto all(Aw &&... aw) noexcept
Build an awaitable that waits for all of the provided awaitables.
Definition awaiters.hpp:200
decltype(auto) get_awaiter(U &&u) noexcept
for an awaitable a, return the actual Awaitable object returned by co_await
Definition awaiters.hpp:225
ReadBuffer< T > read(Async< T > const &a)
Convenience helper that forwards to Async<T>::read().
Definition async/async.hpp:540
auto write_to(WriteBuffer< T > &&buffer, Value value)
Create a coroutine awaiter that writes a value to a WriteBuffer<T>.
Definition awaiters.hpp:409
constexpr auto try_await(Aw &aw) noexcept
Build a non-blocking awaiter that returns optional<T> instead of suspending.
Definition awaiters.hpp:349
Awaitable that waits for all provided awaiters to complete.
Definition awaiters.hpp:93
void await_suspend(TaskFactory f) noexcept
Suspend the coroutine on awaiters not yet ready.
Definition awaiters.hpp:119
bool await_ready() noexcept
Check if all awaiters are ready.
Definition awaiters.hpp:100
int num_awaiters() const noexcept
Return the number of awaiters, needed by the TaskFactory model.
Definition awaiters.hpp:104
auto await_resume()
Resume all awaiters and collect their results.
Definition awaiters.hpp:129
std::tuple< Aw... > bufs_
Underlying awaiters.
Definition awaiters.hpp:94
Awaiter wrapper that returns an optional result.
Definition awaiters.hpp:244
constexpr TryAwaiter(Awt aw) noexcept
Definition awaiters.hpp:250
StoredAwaiter awaiter_
Definition awaiters.hpp:248
void await_suspend(BasicTask t) noexcept
Definition awaiters.hpp:254
std::remove_reference_t< Awt > Awaiter
Definition awaiters.hpp:245
bool await_ready() const noexcept
Definition awaiters.hpp:252
auto await_resume()
Definition awaiters.hpp:256
std::conditional_t< std::is_lvalue_reference_v< Awt >, Awaiter *, Awt > StoredAwaiter
Definition awaiters.hpp:246
Always-ready awaitable owning an immediate operation value.
Definition awaiters.hpp:27
void release() const noexcept
Match the read-buffer release surface without owning an epoch.
Definition awaiters.hpp:45
T await_resume() &&noexcept
Move the value when the awaiter itself is consumed.
Definition awaiters.hpp:42
bool await_ready() const noexcept
Report that the immediate value never suspends.
Definition awaiters.hpp:33
T value_type
Definition awaiters.hpp:28
T const & await_resume() const &noexcept
Return a borrowed reference when the awaiter is retained.
Definition awaiters.hpp:39
T value
Definition awaiters.hpp:30
void await_suspend(BasicTask) const noexcept
No-op suspension hook required by the awaiter protocol.
Definition awaiters.hpp:36
Async aliases for lazy tensor-level views.