Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
experimental_async_test.cc
Go to the documentation of this file.
1/*
2 Aleph_w
3
4 Data structures & Algorithms
5 version 2.0.0b
6 https://github.com/lrleon/Aleph-w
7
8 This file is part of Aleph-w library
9
10 Copyright (c) 2002-2026 Leandro Rabindranath Leon
11
12 Permission is hereby granted, free of charge, to any person obtaining a copy
13 of this software and associated documentation files (the "Software"), to deal
14 in the Software without restriction, including without limitation the rights
15 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 copies of the Software, and to permit persons to whom the Software is
17 furnished to do so, subject to the following conditions:
18
19 The above copyright notice and this permission notice shall be included in all
20 copies or substantial portions of the Software.
21
22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28 SOFTWARE.
29*/
30
31#include <gtest/gtest.h>
32
33#include <experimental_async.H>
34#include <thread_pool.H>
35
36#include <memory>
37#include <stdexcept>
38
39#if ALEPH_HAS_EXPERIMENTAL_ASYNC
40# include <coroutine>
41# include <future>
42#endif
43
44using namespace Aleph;
45
46#if ALEPH_HAS_EXPERIMENTAL_ASYNC
47namespace
48{
49 class detached_task
50 {
51 public:
52 struct promise_type
53 {
55 {
56 return detached_task(
57 std::coroutine_handle<promise_type>::from_promise(*this));
58 }
59
60 std::suspend_never initial_suspend() noexcept { return {}; }
61 std::suspend_always final_suspend() noexcept { return {}; }
62 void return_void() noexcept {}
63 void unhandled_exception() { std::terminate(); }
64 };
65
66 private:
67 std::coroutine_handle<promise_type> handle_;
68
69 explicit detached_task(std::coroutine_handle<promise_type> handle) noexcept
70 : handle_(handle) {}
71
72 public:
73 detached_task(const detached_task &) = delete;
74 detached_task & operator = (const detached_task &) = delete;
75
77 : handle_(other.handle_)
78 {
79 other.handle_ = {};
80 }
81
82 detached_task & operator = (detached_task && other) noexcept
83 {
84 if (this == &other)
85 return *this;
86 if (handle_)
87 handle_.destroy();
88 handle_ = other.handle_;
89 other.handle_ = {};
90 return *this;
91 }
92
94 {
95 if (handle_)
96 handle_.destroy();
97 }
98 };
99
100 template <typename T>
101 class sync_task
102 {
103 std::future<T> future_;
104
105 public:
106 struct promise_type
107 {
108 std::promise<T> promise_;
109
111 {
112 return sync_task(promise_.get_future(),
113 std::coroutine_handle<promise_type>::from_promise(*this));
114 }
115
116 std::suspend_never initial_suspend() noexcept { return {}; }
117 std::suspend_always final_suspend() noexcept { return {}; }
118
119 void return_value(T value)
120 {
121 promise_.set_value(std::move(value));
122 }
123
125 {
126 promise_.set_exception(std::current_exception());
127 }
128 };
129
130 private:
131 std::coroutine_handle<promise_type> handle_;
132
133 sync_task(std::future<T> future,
134 std::coroutine_handle<promise_type> handle) noexcept
135 : future_(std::move(future)), handle_(handle) {}
136
137 public:
138 sync_task(const sync_task &) = delete;
139 sync_task & operator = (const sync_task &) = delete;
140
142 : future_(std::move(other.future_)), handle_(other.handle_)
143 {
144 other.handle_ = {};
145 }
146
147 sync_task & operator = (sync_task && other) noexcept
148 {
149 if (this == &other)
150 return *this;
151 if (handle_)
152 handle_.destroy();
153 future_ = std::move(other.future_);
154 handle_ = other.handle_;
155 other.handle_ = {};
156 return *this;
157 }
158
159 ~sync_task()
160 {
161 if (handle_)
162 handle_.destroy();
163 }
164
165 T get()
166 {
167 return future_.get();
168 }
169 };
170
172 {
173 const int first = co_await experimental::schedule(pool, [] { return 20; });
174 const int second = co_await experimental::schedule(pool, [first] {
175 return first + 22;
176 });
177 co_return second;
178 }
179
181 std::shared_future<void> gate,
182 std::promise<void> started,
183 std::atomic<bool> & resumed)
184 {
185 co_await experimental::schedule(pool, [gate, started = std::move(started)]() mutable {
186 started.set_value();
187 gate.wait();
188 return 11;
189 });
190 resumed.store(true, std::memory_order_release);
191 }
192} // namespace
193
195{
196 ThreadPool pool(2);
197 auto op = experimental::schedule(pool, [] { return 42; });
198
199 EXPECT_EQ(op.get(), 42);
200}
201
203{
204 ThreadPool pool(2);
205 auto op = experimental::schedule(pool, []() -> int {
206 throw std::runtime_error("boom");
207 });
208
209 EXPECT_THROW(op.get(), std::runtime_error);
210}
211
213{
214 ThreadPool pool(2);
215 auto op = experimental::schedule(pool, [] {
216 return std::make_unique<int>(17);
217 });
218
219 auto value = op.get();
220 ASSERT_TRUE(value != nullptr);
221 EXPECT_EQ(*value, 17);
222}
223
225{
226 ThreadPool pool(4);
227
228 EXPECT_EQ(chained_sum(pool).get(), 42);
229}
230
232{
233 ThreadPool pool(2);
234 auto op = experimental::schedule(pool, [] { return 5; });
235
236 EXPECT_TRUE(op.valid());
237 EXPECT_EQ(op.get(), 5);
238 EXPECT_FALSE(op.valid());
239 EXPECT_THROW((void) op.get(), std::runtime_error);
240}
241
243{
244 ThreadPool pool(2);
245 auto op = experimental::schedule(pool, [] {});
246
247 EXPECT_TRUE(op.valid());
248 op.get();
249 EXPECT_FALSE(op.valid());
250 EXPECT_THROW(op.get(), std::runtime_error);
251}
252
254{
255 ThreadPool pool(2);
256 std::promise<void> gate_promise;
257 auto gate = gate_promise.get_future().share();
258 std::promise<void> started;
259 auto started_future = started.get_future();
260 std::atomic<bool> resumed{false};
261
262 {
263 auto task = await_and_mark(pool, gate, std::move(started), resumed);
264 started_future.wait();
265 }
266
267 gate_promise.set_value();
268 std::this_thread::sleep_for(std::chrono::milliseconds(20));
269
270 EXPECT_FALSE(resumed.load(std::memory_order_acquire));
271}
272#else
277#endif
A reusable thread pool for efficient parallel task execution.
#define TEST(name)
Opt-in experimental coroutine-friendly bridge over ThreadPool.
#define ALEPH_HAS_EXPERIMENTAL_ASYNC
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
Divide_Conquer_DP_Result< Cost > divide_and_conquer_partition_dp(const size_t groups, const size_t n, Transition_Cost_Fn transition_cost, const Cost inf=dp_optimization_detail::default_inf< Cost >())
Optimize partition DP using divide-and-conquer optimization.
std::decay_t< typename HeadC::Item_Type > T
Definition ah-zip.H:105
A modern, efficient thread pool for parallel task execution.