152
|
1 // { dg-additional-options "-std=c++17 -w" }
|
|
2
|
|
3 #include <utility>
|
|
4 #include <type_traits>
|
|
5 #include <tuple>
|
|
6 #include <functional>
|
|
7 #include <coroutine>
|
|
8
|
|
9 struct any {
|
|
10 template <typename T> any(T &&) noexcept;
|
|
11 };
|
|
12
|
|
13 template <typename T>
|
|
14 auto get_awaiter_impl(T &&value, int) noexcept
|
|
15 -> decltype(static_cast<T &&>(value).operator co_await()) {
|
|
16 return static_cast<T &&>(value).operator co_await();
|
|
17 }
|
|
18 template <typename T, int = 0>
|
|
19 T &&get_awaiter_impl(T &&value, any) noexcept;
|
|
20 template <typename T>
|
|
21 auto get_awaiter(T &&value) noexcept
|
|
22 -> decltype(get_awaiter_impl(static_cast<T &&>(value), 123)) {
|
|
23 return get_awaiter_impl(static_cast<T &&>(value), 123);
|
|
24 }
|
|
25
|
|
26 template <typename T, typename = void> struct awaitable_traits {
|
|
27 using awaiter_t = decltype(get_awaiter(std::declval<T>()));
|
|
28 using await_result_t = decltype(std::declval<awaiter_t>().await_resume());
|
|
29 };
|
|
30
|
|
31 template <typename TASK_CONTAINER> class when_all_ready_awaitable;
|
|
32 template <typename... TASKS>
|
|
33 class when_all_ready_awaitable<std::tuple<TASKS...>> {
|
|
34 public:
|
|
35 explicit when_all_ready_awaitable(std::tuple<TASKS...> &&tasks) noexcept
|
|
36 : m_tasks(std::move(tasks)) {}
|
|
37 auto operator co_await() &&noexcept {
|
|
38 struct awaiter {
|
|
39 awaiter(when_all_ready_awaitable &awaitable) noexcept
|
|
40 : m_awaitable(awaitable) {}
|
|
41 bool await_ready() const noexcept { return false; }
|
|
42 bool await_suspend() noexcept { return false; }
|
|
43 std::tuple<TASKS...> &&await_resume() noexcept {
|
|
44 return std::move(m_awaitable.m_tasks);
|
|
45 }
|
|
46 when_all_ready_awaitable& m_awaitable;
|
|
47 };
|
|
48 return awaiter{*this};
|
|
49 }
|
|
50 std::tuple<TASKS...> m_tasks;
|
|
51 };
|
|
52
|
|
53 inline void *operator new(std::size_t, void *__p) noexcept;
|
|
54
|
|
55 template <typename RESULT>
|
|
56 class when_all_task_promise final{
|
|
57 public:
|
|
58 using coroutine_handle_t = std::coroutine_handle<when_all_task_promise>;
|
|
59 RESULT &&result() &&;
|
|
60 };
|
|
61 template <typename RESULT> class when_all_task final {
|
|
62 public:
|
|
63 using promise_type = when_all_task_promise<RESULT>;
|
|
64 using coroutine_handle_t = typename promise_type::coroutine_handle_t;
|
|
65 decltype(auto) result() &;
|
|
66 decltype(auto) result() && {
|
|
67 return std::move(m_coroutine.promise()).result();
|
|
68 }
|
|
69 decltype(auto) non_void_result() && {
|
|
70 if constexpr (std::is_void_v<decltype(0)>)
|
|
71 ;
|
|
72 else
|
|
73 return std::move(*this).result();
|
|
74 }
|
|
75 coroutine_handle_t m_coroutine;
|
|
76 };
|
|
77 class task;
|
|
78 template <typename AWAITABLE,
|
|
79 typename RESULT =
|
|
80 typename awaitable_traits<AWAITABLE &&>::await_result_t,
|
|
81 std::enable_if_t<!std::is_void_v<RESULT>, int> = 0>
|
|
82 when_all_task<RESULT> make_when_all_task(AWAITABLE awaitable);
|
|
83
|
|
84 template <typename... AWAITABLES>
|
|
85 inline auto when_all_ready(AWAITABLES &&... awaitables) {
|
|
86 return when_all_ready_awaitable<
|
|
87 std::tuple<when_all_task<typename awaitable_traits<
|
|
88 std::remove_reference_t<AWAITABLES>>::await_result_t>...>>(
|
|
89 std::make_tuple(
|
|
90 make_when_all_task(std::forward<AWAITABLES>(awaitables))...));
|
|
91 }
|
|
92
|
|
93 template <typename FUNC, typename AWAITABLE> class fmap_awaiter {
|
|
94 using awaiter_t = typename awaitable_traits<AWAITABLE &&>::awaiter_t;
|
|
95
|
|
96 public:
|
|
97 fmap_awaiter(FUNC &&func, AWAITABLE &&awaitable) noexcept
|
|
98 : m_func(static_cast<FUNC &&>(func)),
|
|
99 m_awaiter(get_awaiter(static_cast<AWAITABLE &&>(awaitable))) {}
|
|
100 decltype(auto) await_ready() noexcept {
|
|
101 return static_cast<awaiter_t &&>(m_awaiter).await_ready();
|
|
102 }
|
|
103 template <typename PROMISE>
|
|
104 decltype(auto) await_suspend(std::coroutine_handle<PROMISE> coro) noexcept {}
|
|
105 template <typename AWAIT_RESULT =
|
|
106 decltype(std::declval<awaiter_t>().await_resume()),
|
|
107 std::enable_if_t<!std::is_void_v<AWAIT_RESULT>, int> = 0>
|
|
108 decltype(auto) await_resume() noexcept {
|
|
109 return std::invoke(static_cast<FUNC &&>(m_func),
|
|
110 static_cast<awaiter_t &&>(m_awaiter).await_resume());
|
|
111 }
|
|
112
|
|
113 private:
|
|
114 FUNC &&m_func;
|
|
115 awaiter_t m_awaiter;
|
|
116 };
|
|
117 template <typename FUNC, typename AWAITABLE> class fmap_awaitable {
|
|
118 public:
|
|
119 template <
|
|
120 typename FUNC_ARG, typename AWAITABLE_ARG,
|
|
121 std::enable_if_t<std::is_constructible_v<FUNC, FUNC_ARG &&> &&
|
|
122 std::is_constructible_v<AWAITABLE, AWAITABLE_ARG &&>,
|
|
123 int> = 0>
|
|
124 explicit fmap_awaitable(FUNC_ARG &&func, AWAITABLE_ARG &&awaitable) noexcept
|
|
125 : m_func(static_cast<FUNC_ARG &&>(func)),
|
|
126 m_awaitable(static_cast<AWAITABLE_ARG &&>(awaitable)) {}
|
|
127 auto operator co_await() && {
|
|
128 return fmap_awaiter(static_cast<FUNC &&>(m_func),
|
|
129 static_cast<AWAITABLE &&>(m_awaitable));
|
|
130 }
|
|
131
|
|
132 private:
|
|
133 FUNC m_func;
|
|
134 AWAITABLE m_awaitable;
|
|
135 };
|
|
136
|
|
137 template <typename FUNC, typename AWAITABLE>
|
|
138 auto fmap(FUNC &&func, AWAITABLE &&awaitable) {
|
|
139 return fmap_awaitable<std::remove_cv_t<std::remove_reference_t<FUNC>>,
|
|
140 std::remove_cv_t<std::remove_reference_t<AWAITABLE>>>(
|
|
141 std::forward<FUNC>(func), std::forward<AWAITABLE>(awaitable));
|
|
142 }
|
|
143 template <typename... AWAITABLES>
|
|
144 auto when_all(AWAITABLES &&... awaitables) {
|
|
145 return fmap(
|
|
146 [](auto &&taskTuple) {
|
|
147 decltype(auto) __trans_tmp_1 = std::apply(
|
|
148 [](auto &&... tasks) {
|
|
149 return std::make_tuple(
|
|
150 static_cast<decltype(tasks)>(tasks).non_void_result()...);
|
|
151 },
|
|
152 static_cast<decltype(taskTuple)>(taskTuple));
|
|
153 return __trans_tmp_1;
|
|
154 },
|
|
155 when_all_ready(std::forward<AWAITABLES>(awaitables)...));
|
|
156 }
|
|
157 class async_mutex_scoped_lock_operation;
|
|
158 class async_mutex {
|
|
159 public:
|
|
160 async_mutex() noexcept;
|
|
161 async_mutex_scoped_lock_operation scoped_lock_async() noexcept;
|
|
162 };
|
|
163 class async_mutex_lock {
|
|
164 public:
|
|
165 explicit async_mutex_lock();
|
|
166 ~async_mutex_lock();
|
|
167
|
|
168 private:
|
|
169 async_mutex *m_mutex;
|
|
170 };
|
|
171 class async_mutex_scoped_lock_operation {
|
|
172 public:
|
|
173 async_mutex_lock await_resume() const noexcept;
|
|
174 };
|
|
175 class task {
|
|
176 public:
|
|
177 class promise_type {
|
|
178 public:
|
|
179 auto initial_suspend() noexcept { return std::suspend_always{}; }
|
|
180 auto final_suspend() noexcept { return std::suspend_always{}; }
|
|
181 task get_return_object() noexcept { return task{}; }
|
|
182 void unhandled_exception() noexcept {}
|
|
183 void return_value(int value) noexcept { v = value; }
|
|
184 int result(){ return v; }
|
|
185 int v = 0;
|
|
186 };
|
|
187 public:
|
|
188 task() noexcept {}
|
|
189 auto operator co_await() const &noexcept {
|
|
190 struct awaitable {
|
|
191 std::coroutine_handle<promise_type> m_coroutine;
|
|
192 decltype(auto) await_resume() {
|
|
193 return this->m_coroutine.promise().result();
|
|
194 }
|
|
195 };
|
|
196 return awaitable{};
|
|
197 }
|
|
198 };
|
|
199 void foo() {
|
|
200 (void) []() -> task {
|
|
201 auto makeTask = [](int x) -> task { co_return x; };
|
|
202 async_mutex_scoped_lock_operation op;
|
|
203 co_await when_all(std::move(op), makeTask(123));
|
|
204 }();
|
|
205 }
|