libcopp 2.3.1
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
callable_promise_test.cpp
Go to the documentation of this file.
1// Copyright 2023 owent
2
5
6#include <cstdio>
7#include <cstring>
8#include <iostream>
9#include <list>
10#include <string>
11
12#include "frame/test_macros.h"
13
14#if defined(LIBCOPP_MACRO_ENABLE_STD_COROUTINE) && LIBCOPP_MACRO_ENABLE_STD_COROUTINE
15
16struct callable_promise_test_pending_awaitable {
17 bool await_ready() noexcept { return false; }
18
19 void await_resume() noexcept { detach(); }
20
21# if defined(LIBCOPP_MACRO_ENABLE_CONCEPTS) && LIBCOPP_MACRO_ENABLE_CONCEPTS
22 template <copp::DerivedPromiseBaseType TPROMISE>
23# else
24 template <class TPROMISE, typename = std::enable_if_t<std::is_base_of<copp::promise_base_type, TPROMISE>::value>>
25# endif
26 void await_suspend(LIBCOPP_MACRO_STD_COROUTINE_NAMESPACE coroutine_handle<TPROMISE> caller) noexcept {
27 pending.push_back(caller);
28 current = caller;
29 }
30
31 void detach() noexcept {
32 if (!current) {
33 return;
34 }
35
36 for (auto iter = pending.begin(); iter != pending.end(); ++iter) {
37 if (*iter == current) {
38 pending.erase(iter);
39 current = nullptr;
40 break;
41 }
42 }
43 }
44
45 callable_promise_test_pending_awaitable() {}
46 ~callable_promise_test_pending_awaitable() {
47 // detach when destroyed
48 detach();
49 }
50
51 LIBCOPP_MACRO_STD_COROUTINE_NAMESPACE coroutine_handle<> current = nullptr;
52
53 static std::list<LIBCOPP_MACRO_STD_COROUTINE_NAMESPACE coroutine_handle<>> pending;
54 static void resume_all() {
55 while (!pending.empty()) {
56 pending.front().resume();
57 }
58 }
59
60 static void resume_some(int max_count) {
61 while (!pending.empty() && max_count-- > 0) {
62 pending.front().resume();
63 }
64 }
65};
66std::list<LIBCOPP_MACRO_STD_COROUTINE_NAMESPACE coroutine_handle<>> callable_promise_test_pending_awaitable::pending;
67
68static copp::callable_future<int> callable_func_int_l1(int inout) {
69 CASE_MSG_INFO() << "callable inner future ready int: " << inout << std::endl;
70 co_return inout;
71}
72
73static copp::callable_future<int> callable_func_int_l2(int inout) {
74 CASE_MSG_INFO() << "callable inner future async int: " << inout << std::endl;
75 co_await callable_promise_test_pending_awaitable();
76 CASE_MSG_INFO() << "callable inner future return int: " << inout << std::endl;
77 co_return inout;
78}
79
80static copp::callable_future<int> callable_func_await_int() {
81 auto v = callable_func_int_l1(3);
82 auto u = callable_func_int_l2(11);
83 CASE_MSG_INFO() << "callable await int" << std::endl;
84 int x = (co_await v + co_await u);
85 CASE_MSG_INFO() << "callable return int" << std::endl;
86 co_return x;
87}
88
89CASE_TEST(callable_promise, callable_future_integer_need_resume) {
90 copp::callable_future<int> f = callable_func_await_int();
91 CASE_EXPECT_EQ(static_cast<int>(copp::promise_status::kRunning), static_cast<int>(f.get_status()));
92
93 CASE_EXPECT_FALSE(f.is_ready());
94
95 callable_promise_test_pending_awaitable::resume_all();
96 CASE_EXPECT_EQ(static_cast<int>(copp::promise_status::kDone), static_cast<int>(f.get_status()));
97 CASE_EXPECT_TRUE(f.is_ready());
98 CASE_EXPECT_EQ(14, f.get_internal_promise().data());
99}
100
101static copp::callable_future<int> callable_func_await_int_ready() {
102 auto v = callable_func_int_l1(33);
103 auto u = callable_func_int_l1(31);
104 CASE_MSG_INFO() << "callable await int no wait" << std::endl;
105 int x = (co_await v + co_await u);
106 CASE_MSG_INFO() << "callable return int no wait" << std::endl;
107 co_return x;
108}
109
110CASE_TEST(callable_promise, callable_future_integer_ready) {
111 copp::callable_future<int> f = callable_func_await_int_ready();
112
113 CASE_EXPECT_EQ(static_cast<int>(copp::promise_status::kDone), static_cast<int>(f.get_status()));
114 CASE_EXPECT_TRUE(f.is_ready());
115 CASE_EXPECT_EQ(64, f.get_internal_promise().data());
116}
117
118static copp::callable_future<void> callable_func_void_l1() {
119 CASE_MSG_INFO() << "callable inner future ready void" << std::endl;
120 co_return;
121}
122
123static copp::callable_future<void> callable_func_void_l2() {
124 CASE_MSG_INFO() << "callable inner future async void" << std::endl;
125 co_await callable_promise_test_pending_awaitable();
126 CASE_MSG_INFO() << "callable inner future return void" << std::endl;
127 co_return;
128}
129
130static copp::callable_future<void> callable_func_await_void() {
131 CASE_MSG_INFO() << "callable await void" << std::endl;
132 co_await callable_func_void_l1();
133 co_await callable_func_void_l2();
134 CASE_MSG_INFO() << "callable return void" << std::endl;
135 co_return;
136}
137
138CASE_TEST(callable_promise, callable_future_void_need_resume) {
139 copp::callable_future<void> f = callable_func_await_void();
140 CASE_EXPECT_EQ(static_cast<int>(copp::promise_status::kRunning), static_cast<int>(f.get_status()));
141
142 CASE_EXPECT_FALSE(f.is_ready());
143 callable_promise_test_pending_awaitable::resume_all();
144 CASE_EXPECT_EQ(static_cast<int>(copp::promise_status::kDone), static_cast<int>(f.get_status()));
145 CASE_EXPECT_TRUE(f.is_ready());
146}
147
148static copp::callable_future<void> callable_func_await_void_ready() {
149 CASE_MSG_INFO() << "callable await void no wait" << std::endl;
150 co_await callable_func_void_l1();
151 co_await callable_func_void_l1();
152 CASE_MSG_INFO() << "callable return void no wait" << std::endl;
153 co_return;
154}
155
156CASE_TEST(callable_promise, callable_future_void_ready) {
157 copp::callable_future<void> f = callable_func_await_void_ready();
158 CASE_EXPECT_EQ(static_cast<int>(copp::promise_status::kDone), static_cast<int>(f.get_status()));
159 CASE_EXPECT_TRUE(f.is_ready());
160
161 callable_promise_test_pending_awaitable::resume_all();
162}
163
164static copp::callable_future<int> callable_func_int_await_void() {
165 CASE_MSG_INFO() << "callable int await void: start" << std::endl;
166 co_await callable_func_void_l1();
167 co_await callable_func_void_l2();
168 auto v = callable_func_int_l1(17);
169 auto u = callable_func_int_l2(23);
170 int x = (co_await v + co_await u);
171 CASE_MSG_INFO() << "callable int await void: return" << std::endl;
172 co_return x;
173}
174
175CASE_TEST(callable_promise, callable_future_int_await_void) {
176 copp::callable_future<int> f = callable_func_int_await_void();
177 CASE_EXPECT_EQ(static_cast<int>(copp::promise_status::kRunning), static_cast<int>(f.get_status()));
178
179 CASE_EXPECT_FALSE(f.is_ready());
180 callable_promise_test_pending_awaitable::resume_all();
181 CASE_EXPECT_EQ(static_cast<int>(copp::promise_status::kDone), static_cast<int>(f.get_status()));
182 CASE_EXPECT_TRUE(f.is_ready());
183 CASE_EXPECT_EQ(40, f.get_internal_promise().data());
184}
185
186static copp::callable_future<int> callable_func_killed_by_caller_l3() {
187 co_await callable_promise_test_pending_awaitable();
188 auto current_status = co_yield copp::callable_future<int>::yield_status();
189 CASE_EXPECT_TRUE(copp::promise_status::kKilled == current_status);
190
191 // await again and return immdiately
192 // co_await callable_promise_test_pending_awaitable();
193 co_return -static_cast<int>(current_status);
194}
195
196static copp::callable_future<int> callable_func_killed_by_caller_l2() {
197 int result = co_await callable_func_killed_by_caller_l3();
198 co_return result;
199}
200
201static copp::callable_future<int> callable_func_killed_by_caller_l1() {
202 int result = co_await callable_func_killed_by_caller_l2();
203 co_return result;
204}
205
206CASE_TEST(callable_promise, killed_by_caller_resume_waiting) {
207 copp::callable_future<int> f = callable_func_killed_by_caller_l1();
208 CASE_EXPECT_EQ(static_cast<int>(copp::promise_status::kRunning), static_cast<int>(f.get_status()));
209
210 CASE_EXPECT_FALSE(f.is_ready());
211
212 // Mock to kill by caller
213 f.kill(copp::promise_status::kKilled, true);
214 CASE_EXPECT_TRUE(f.is_ready());
215 CASE_EXPECT_EQ(f.get_internal_promise().data(), -static_cast<int>(copp::promise_status::kKilled));
216
217 // cleanup
218 callable_promise_test_pending_awaitable::resume_all();
219 CASE_EXPECT_EQ(static_cast<int>(copp::promise_status::kKilled), static_cast<int>(f.get_status()));
220}
221
222CASE_TEST(callable_promise, killed_by_caller_drop_generator) {
223 copp::callable_future<int> f = callable_func_killed_by_caller_l2();
224 CASE_EXPECT_EQ(static_cast<int>(copp::promise_status::kRunning), static_cast<int>(f.get_status()));
225
226 CASE_EXPECT_FALSE(f.is_ready());
227
228 // Mock to kill by caller
229 f.kill(copp::promise_status::kKilled, true);
230 CASE_EXPECT_TRUE(f.is_ready());
231 CASE_EXPECT_EQ(f.get_internal_promise().data(), -static_cast<int>(copp::promise_status::kKilled));
232
233 // cleanup
234 callable_promise_test_pending_awaitable::resume_all();
235 CASE_EXPECT_EQ(static_cast<int>(copp::promise_status::kKilled), static_cast<int>(f.get_status()));
236}
237
238static copp::callable_future<int> callable_func_some_any_all_callable_suspend(int value) {
239 co_await callable_promise_test_pending_awaitable();
240 co_return value;
241}
242
243static copp::callable_future<int> callable_func_some_callable_in_container(size_t expect_ready_count,
244 copp::promise_status expect_status) {
245 size_t resume_ready_count = 0;
246
247 std::vector<copp::callable_future<int>> callables;
248 callables.emplace_back(callable_func_some_any_all_callable_suspend(471));
249 callables.emplace_back(callable_func_some_any_all_callable_suspend(473));
250 callables.emplace_back(callable_func_some_any_all_callable_suspend(477));
251
252 copp::some_ready<copp::callable_future<int>>::type readys;
253 auto some_result = co_await copp::some(readys, 2, copp::gsl::make_span(callables));
254 CASE_EXPECT_EQ(static_cast<int>(expect_status), static_cast<int>(some_result));
255
256 int result = 1;
257 for (auto &ready_callable : readys) {
258 if (ready_callable->is_ready()) {
259 result += ready_callable->get_internal_promise().data();
260 ++resume_ready_count;
261 }
262 }
263
264 CASE_EXPECT_EQ(expect_ready_count, resume_ready_count);
265
266 // Nothing happend here if we await the callables again.
267 some_result = co_await copp::some(readys, 2, callables);
268 CASE_EXPECT_EQ(static_cast<int>(expect_status), static_cast<int>(some_result));
269
270 // If it's killed, await will trigger suspend and resume again, or it will return directly.
271 CASE_EXPECT_EQ(expect_ready_count, resume_ready_count);
272
273 co_return result;
274}
275
276CASE_TEST(callable_promise, finish_some_in_container) {
277 auto f = callable_func_some_callable_in_container(2, copp::promise_status::kDone);
278
279 CASE_EXPECT_FALSE(f.is_ready());
280
281 // partly resume
282 callable_promise_test_pending_awaitable::resume_some(1);
283 CASE_EXPECT_FALSE(f.is_ready());
284 callable_promise_test_pending_awaitable::resume_some(1);
285
286 CASE_EXPECT_TRUE(f.is_ready());
287 CASE_EXPECT_EQ(945, f.get_internal_promise().data());
288
289 callable_promise_test_pending_awaitable::resume_all();
290}
291
292CASE_TEST(callable_promise, kill_some_in_container) {
293 auto f = callable_func_some_callable_in_container(0, copp::promise_status::kKilled);
294
295 CASE_EXPECT_FALSE(f.is_ready());
296
297 // partly resume
298 f.kill();
299
300 CASE_EXPECT_TRUE(f.is_ready());
301 CASE_EXPECT_EQ(1, f.get_internal_promise().data());
302
303 callable_promise_test_pending_awaitable::resume_all();
304}
305
306static copp::callable_future<int> callable_func_some_callable_in_initialize_list(size_t expect_ready_count,
307 copp::promise_status expect_status) {
308 size_t resume_ready_count = 0;
309
310 copp::callable_future<int> callable1 = callable_func_some_any_all_callable_suspend(471);
311 copp::callable_future<int> callable2 = callable_func_some_any_all_callable_suspend(473);
312 copp::callable_future<int> callable3 = callable_func_some_any_all_callable_suspend(477);
313
314 copp::some_ready<copp::callable_future<int>>::type readys;
315 std::reference_wrapper<copp::callable_future<int>> pending[] = {callable1, callable2, callable3};
316 auto some_result = co_await copp::some(readys, 2, copp::gsl::make_span(pending));
317 CASE_EXPECT_EQ(static_cast<int>(expect_status), static_cast<int>(some_result));
318
319 int result = 1;
320 for (auto &ready_callable : readys) {
321 if (ready_callable->is_ready()) {
322 result += ready_callable->get_internal_promise().data();
323 ++resume_ready_count;
324 }
325 }
326
327 CASE_EXPECT_EQ(expect_ready_count, resume_ready_count);
328
329 co_return result;
330}
331
332CASE_TEST(callable_promise, finish_some_in_initialize_list) {
333 auto f = callable_func_some_callable_in_initialize_list(2, copp::promise_status::kDone);
334
335 CASE_EXPECT_FALSE(f.is_ready());
336
337 // partly resume
338 callable_promise_test_pending_awaitable::resume_some(1);
339 CASE_EXPECT_FALSE(f.is_ready());
340 callable_promise_test_pending_awaitable::resume_some(1);
341
342 CASE_EXPECT_TRUE(f.is_ready());
343 CASE_EXPECT_EQ(945, f.get_internal_promise().data());
344
345 callable_promise_test_pending_awaitable::resume_all();
346}
347
348static copp::callable_future<int> callable_func_any_callable_in_container(size_t expect_ready_count,
349 copp::promise_status expect_status) {
350 size_t resume_ready_count = 0;
351
352 std::vector<copp::callable_future<int>> callables;
353 callables.emplace_back(callable_func_some_any_all_callable_suspend(671));
354 callables.emplace_back(callable_func_some_any_all_callable_suspend(673));
355 callables.emplace_back(callable_func_some_any_all_callable_suspend(677));
356
357 copp::any_ready<copp::callable_future<int>>::type readys;
358 auto any_result = co_await copp::any(readys, copp::gsl::make_span(&callables[0], &callables[0] + callables.size()));
359 CASE_EXPECT_EQ(static_cast<int>(expect_status), static_cast<int>(any_result));
360
361 int result = 1;
362 for (auto &ready_callable : readys) {
363 if (ready_callable->is_ready()) {
364 result += ready_callable->get_internal_promise().data();
365 ++resume_ready_count;
366 }
367 }
368
369 CASE_EXPECT_EQ(expect_ready_count, resume_ready_count);
370
371 // Nothing happend here if we await the callables again.
372 any_result = co_await copp::any(readys, callables);
373 CASE_EXPECT_EQ(static_cast<int>(expect_status), static_cast<int>(any_result));
374
375 // If it's killed, await will trigger suspend and resume again, or it will return directly.
376 CASE_EXPECT_EQ(expect_ready_count, resume_ready_count);
377
378 co_return result;
379}
380
381CASE_TEST(callable_promise, finish_any_in_container) {
382 auto f = callable_func_any_callable_in_container(1, copp::promise_status::kDone);
383
384 CASE_EXPECT_FALSE(f.is_ready());
385
386 // partly resume
387 callable_promise_test_pending_awaitable::resume_some(1);
388
389 CASE_EXPECT_TRUE(f.is_ready());
390 CASE_EXPECT_EQ(672, f.get_internal_promise().data());
391
392 callable_promise_test_pending_awaitable::resume_all();
393}
394
395static copp::callable_future<int> callable_func_all_callable_in_container(size_t expect_ready_count,
396 copp::promise_status expect_status) {
397 size_t resume_ready_count = 0;
398
399 std::vector<copp::callable_future<int>> callables;
400 callables.emplace_back(callable_func_some_any_all_callable_suspend(671));
401 callables.emplace_back(callable_func_some_any_all_callable_suspend(791));
402 callables.emplace_back(callable_func_some_any_all_callable_suspend(793));
403
404 copp::all_ready<copp::callable_future<int>>::type readys;
405 auto all_result = co_await copp::all(readys, copp::gsl::make_span(callables.data(), callables.size()));
406 CASE_EXPECT_EQ(static_cast<int>(expect_status), static_cast<int>(all_result));
407
408 int result = 1;
409 for (auto &ready_callable : readys) {
410 if (ready_callable->is_ready()) {
411 result += ready_callable->get_internal_promise().data();
412 ++resume_ready_count;
413 }
414 }
415
416 CASE_EXPECT_EQ(expect_ready_count, resume_ready_count);
417
418 // Nothing happend here if we await the callables again.
419 all_result = co_await copp::all(readys, callables);
420 CASE_EXPECT_EQ(static_cast<int>(expect_status), static_cast<int>(all_result));
421
422 // If it's killed, await will trigger suspend and resume again, or it will return directly.
423 CASE_EXPECT_EQ(expect_ready_count, resume_ready_count);
424
425 co_return result;
426}
427
428CASE_TEST(callable_promise, finish_all_in_container) {
429 auto f = callable_func_all_callable_in_container(3, copp::promise_status::kDone);
430
431 CASE_EXPECT_FALSE(f.is_ready());
432
433 // partly resume
434 callable_promise_test_pending_awaitable::resume_some(1);
435 CASE_EXPECT_FALSE(f.is_ready());
436
437 callable_promise_test_pending_awaitable::resume_all();
438
439 CASE_EXPECT_TRUE(f.is_ready());
440 CASE_EXPECT_EQ(2256, f.get_internal_promise().data());
441
442 callable_promise_test_pending_awaitable::resume_all();
443}
444
445#else
446CASE_TEST(callable_promise, disabled) {}
447#endif
#define LIBCOPP_MACRO_STD_COROUTINE_NAMESPACE
Definition coroutine.h:41
#define CASE_MSG_INFO()
#define CASE_EXPECT_FALSE(c)
Definition test_macros.h:98
#define CASE_EXPECT_EQ(l, r)
Definition test_macros.h:99
#define CASE_TEST(test_name, case_name)
Definition test_macros.h:47
#define CASE_EXPECT_TRUE(c)
Definition test_macros.h:97