libcopp 2.3.1
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
generator_promise_test.cpp
Go to the documentation of this file.
1// Copyright 2023 owent
2
6
7#include <array>
8#include <cstdio>
9#include <cstring>
10#include <iostream>
11#include <list>
12#include <string>
13
14#include "frame/test_macros.h"
15
16#if defined(LIBCOPP_MACRO_ENABLE_STD_COROUTINE) && LIBCOPP_MACRO_ENABLE_STD_COROUTINE
17
18using generator_future_int_type = copp::generator_future<int>;
19using generator_future_void_type = copp::generator_future<void>;
20
21using generator_future_int_lightweight_type = copp::generator_lightweight_future<int>;
22using generator_future_void_lightweight_type = copp::generator_lightweight_future<void>;
23
24using generator_future_int_channel_type = copp::generator_channel_future<int, copp::promise_error_transform<int>>;
25
26namespace {
27std::list<generator_future_int_type::context_pointer_type> g_pending_int_contexts;
28std::list<generator_future_void_type::context_pointer_type> g_pending_void_contexts;
29std::list<generator_future_int_lightweight_type::context_pointer_type> g_pending_int_lightweight_contexts;
30std::list<generator_future_void_lightweight_type::context_pointer_type> g_pending_void_lightweight_contexts;
31std::list<generator_future_int_channel_type::context_pointer_type> g_pending_int_channel_contexts;
32size_t g_resume_generator_count = 0;
33size_t g_suspend_generator_count = 0;
34
35size_t resume_pending_contexts(std::list<int> values, int max_count = 32767) {
36 size_t ret = 0;
37 while (max_count > 0 && (!g_pending_int_contexts.empty() || !g_pending_void_contexts.empty() ||
38 !g_pending_int_lightweight_contexts.empty() ||
39 !g_pending_void_lightweight_contexts.empty() || !g_pending_int_channel_contexts.empty())) {
40 --max_count;
41 if (!g_pending_int_contexts.empty()) {
42 auto ctx = *g_pending_int_contexts.begin();
43 g_pending_int_contexts.pop_front();
44
45 if (!values.empty()) {
46 int val = values.front();
47 values.pop_front();
48 ctx->set_value(val);
49 } else {
50 ctx->set_value(0);
51 }
52
53 ++ret;
54 } else if (!g_pending_void_contexts.empty()) {
55 auto ctx = *g_pending_void_contexts.begin();
56 g_pending_void_contexts.pop_front();
57 ctx->set_value();
58
59 ++ret;
60 } else if (!g_pending_int_lightweight_contexts.empty()) {
61 auto ctx = *g_pending_int_lightweight_contexts.begin();
62 g_pending_int_lightweight_contexts.pop_front();
63
64 if (!values.empty()) {
65 int val = values.front();
66 values.pop_front();
67 ctx->set_value(val);
68 } else {
69 ctx->set_value(0);
70 }
71
72 ++ret;
73 } else if (!g_pending_void_lightweight_contexts.empty()) {
74 auto ctx = *g_pending_void_lightweight_contexts.begin();
75 g_pending_void_lightweight_contexts.pop_front();
76 ctx->set_value();
77
78 ++ret;
79 } else if (!g_pending_int_channel_contexts.empty()) {
80 auto ctx = *g_pending_int_channel_contexts.begin();
81 g_pending_int_channel_contexts.pop_front();
82
83 if (!values.empty()) {
84 int val = values.front();
85 values.pop_front();
86 ctx->set_value(val);
87 } else {
88 ctx->set_value(0);
89 }
90
91 ++ret;
92 }
93 }
94
95 return ret;
96}
97
98} // namespace
99
100static copp::callable_future<int> callable_func_await_int() {
101 generator_future_int_type gen_left_value{
102 [](generator_future_int_type::context_pointer_type ctx) {
103 ++g_suspend_generator_count;
104 g_pending_int_contexts.push_back(ctx);
105 },
106 [](const generator_future_int_type::context_type &) { ++g_resume_generator_count; }};
107
108 // await left value
109 CASE_EXPECT_FALSE(gen_left_value.is_ready());
110 CASE_EXPECT_TRUE(gen_left_value.is_pending());
111 CASE_EXPECT_TRUE(gen_left_value.get_status() == copp::promise_status::kRunning);
112 int x1 = co_await gen_left_value;
113 CASE_EXPECT_TRUE(gen_left_value.is_ready());
114 CASE_EXPECT_FALSE(gen_left_value.is_pending());
115 CASE_EXPECT_TRUE(gen_left_value.get_status() == copp::promise_status::kDone);
116
117 // Await a ready generator will be ignored and will not increase suspend count
118 CASE_EXPECT_EQ(x1, co_await gen_left_value);
119
120 // await right value
121 int x2 = co_await generator_future_int_type{
122 [](generator_future_int_type::context_pointer_type ctx) {
123 ++g_suspend_generator_count;
124 g_pending_int_contexts.push_back(ctx);
125 },
126 [](const generator_future_int_type::context_type &) { ++g_resume_generator_count; }};
127
128 generator_future_void_type gen_left_void{
129 [](generator_future_void_type::context_pointer_type ctx) {
130 ++g_suspend_generator_count;
131 g_pending_void_contexts.push_back(ctx);
132 },
133 [](const generator_future_void_type::context_type &) { ++g_resume_generator_count; }};
134
135 // await left value
136 CASE_EXPECT_FALSE(gen_left_void.is_ready());
137 CASE_EXPECT_TRUE(gen_left_void.is_pending());
138 CASE_EXPECT_TRUE(gen_left_void.get_status() == copp::promise_status::kRunning);
139 co_await gen_left_void;
140 CASE_EXPECT_TRUE(gen_left_void.is_ready());
141 CASE_EXPECT_FALSE(gen_left_void.is_pending());
142 CASE_EXPECT_TRUE(gen_left_void.get_status() == copp::promise_status::kDone);
143
144 // Await a ready generator will be ignored and will not increase suspend count
145 co_await gen_left_void;
146
147 // await right value
148 co_await generator_future_void_type{
149 [](generator_future_void_type::context_pointer_type ctx) {
150 ++g_suspend_generator_count;
151 g_pending_void_contexts.push_back(ctx);
152 },
153 [](const generator_future_void_type::context_type &) { ++g_resume_generator_count; }};
154
155 co_return x1 + x2;
156}
157
158CASE_TEST(generator_promise, basic_int_generator) {
159 size_t old_resume_generator_count = g_resume_generator_count;
160 size_t old_suspend_generator_count = g_suspend_generator_count;
161
162 copp::callable_future<int> f = callable_func_await_int();
163
164 CASE_EXPECT_NE(static_cast<int>(copp::promise_status::kDone), static_cast<int>(f.get_status()));
165 CASE_EXPECT_FALSE(f.is_ready());
166
167 resume_pending_contexts({13100, 13});
168
169 CASE_EXPECT_TRUE(f.is_ready());
170 CASE_EXPECT_EQ(13113, f.get_internal_promise().data());
171
172 CASE_EXPECT_EQ(old_resume_generator_count + 4, g_resume_generator_count);
173 CASE_EXPECT_EQ(old_suspend_generator_count + 4, g_suspend_generator_count);
174}
175
176static copp::callable_future<int> callable_func_await_int_lightweight() {
177 generator_future_int_lightweight_type gen_left_value{
178 [](generator_future_int_lightweight_type::context_pointer_type ctx) {
179 ++g_suspend_generator_count;
180 g_pending_int_lightweight_contexts.push_back(ctx);
181 },
182 [](const generator_future_int_lightweight_type::context_type &) { ++g_resume_generator_count; }};
183
184 // await left value
185 CASE_EXPECT_FALSE(gen_left_value.is_ready());
186 CASE_EXPECT_TRUE(gen_left_value.is_pending());
187 CASE_EXPECT_TRUE(gen_left_value.get_status() == copp::promise_status::kRunning);
188 int x1 = co_await gen_left_value;
189 CASE_EXPECT_TRUE(gen_left_value.is_ready());
190 CASE_EXPECT_FALSE(gen_left_value.is_pending());
191 CASE_EXPECT_TRUE(gen_left_value.get_status() == copp::promise_status::kDone);
192
193 // Await a ready generator will be ignored and will not increase suspend count
194 CASE_EXPECT_EQ(x1, co_await gen_left_value);
195
196 // await right value
197 int x2 = co_await generator_future_int_lightweight_type{
198 [](generator_future_int_lightweight_type::context_pointer_type ctx) {
199 ++g_suspend_generator_count;
200 g_pending_int_lightweight_contexts.push_back(ctx);
201 },
202 [](const generator_future_int_lightweight_type::context_type &) { ++g_resume_generator_count; }};
203
204 generator_future_void_lightweight_type gen_left_void{
205 [](generator_future_void_lightweight_type::context_pointer_type ctx) {
206 ++g_suspend_generator_count;
207 g_pending_void_lightweight_contexts.push_back(ctx);
208 },
209 [](const generator_future_void_lightweight_type::context_type &) { ++g_resume_generator_count; }};
210
211 // await left value
212 CASE_EXPECT_FALSE(gen_left_void.is_ready());
213 CASE_EXPECT_TRUE(gen_left_void.is_pending());
214 CASE_EXPECT_TRUE(gen_left_void.get_status() == copp::promise_status::kRunning);
215 co_await gen_left_void;
216 CASE_EXPECT_TRUE(gen_left_void.is_ready());
217 CASE_EXPECT_FALSE(gen_left_void.is_pending());
218 CASE_EXPECT_TRUE(gen_left_void.get_status() == copp::promise_status::kDone);
219
220 // Await a ready generator will be ignored and will not increase suspend count
221 co_await gen_left_void;
222
223 // await right value
224 co_await generator_future_void_lightweight_type{
225 [](generator_future_void_lightweight_type::context_pointer_type ctx) {
226 ++g_suspend_generator_count;
227 g_pending_void_lightweight_contexts.push_back(ctx);
228 },
229 [](const generator_future_void_lightweight_type::context_type &) { ++g_resume_generator_count; }};
230
231 co_return x1 + x2;
232}
233
234CASE_TEST(generator_promise, lightweight_int_generator) {
235 size_t old_resume_generator_count = g_resume_generator_count;
236 size_t old_suspend_generator_count = g_suspend_generator_count;
237
238 copp::callable_future<int> f = callable_func_await_int_lightweight();
239
240 CASE_EXPECT_NE(static_cast<int>(copp::promise_status::kDone), static_cast<int>(f.get_status()));
241 CASE_EXPECT_FALSE(f.is_ready());
242
243 resume_pending_contexts({13107, 15});
244
245 CASE_EXPECT_TRUE(f.is_ready());
246 CASE_EXPECT_EQ(13122, f.get_internal_promise().data());
247
248 CASE_EXPECT_EQ(old_resume_generator_count + 4, g_resume_generator_count);
249 CASE_EXPECT_EQ(old_suspend_generator_count + 4, g_suspend_generator_count);
250}
251
252template <class TVALUE, class TERROR_TRANSFORM>
253static copp::generator_channel_receiver<TVALUE, TERROR_TRANSFORM> callable_func_await_int_channel_pick_reciever(
254 std::pair<copp::generator_channel_receiver<TVALUE, TERROR_TRANSFORM>,
255 copp::generator_channel_sender<TVALUE, TERROR_TRANSFORM>> &&input) {
256 g_pending_int_channel_contexts.emplace_back(std::move(input.second));
257 return std::move(input.first);
258}
259
260static copp::callable_future<int> callable_func_await_int_channel() {
261 auto gen_left_value = callable_func_await_int_channel_pick_reciever(copp::make_channel<int>());
262
263 // await left value
264 CASE_EXPECT_FALSE(gen_left_value.is_ready());
265 CASE_EXPECT_TRUE(gen_left_value.is_pending());
266 CASE_EXPECT_TRUE(gen_left_value.get_status() == copp::promise_status::kRunning);
267 int x1 = co_await gen_left_value;
268 CASE_EXPECT_TRUE(gen_left_value.is_ready());
269 CASE_EXPECT_FALSE(gen_left_value.is_pending());
270 CASE_EXPECT_TRUE(gen_left_value.get_status() == copp::promise_status::kDone);
271
272 // Await a ready generator will be ignored and will not increase suspend count
273 CASE_EXPECT_EQ(x1, co_await gen_left_value);
274
275 // await right value
276 int x2 = co_await callable_func_await_int_channel_pick_reciever(copp::make_channel<int>());
277
278 co_return x1 + x2;
279}
280
281CASE_TEST(generator_promise, channel_int_generator) {
282 copp::callable_future<int> f = callable_func_await_int_channel();
283
284 CASE_EXPECT_NE(static_cast<int>(copp::promise_status::kDone), static_cast<int>(f.get_status()));
285 CASE_EXPECT_FALSE(f.is_ready());
286
287 resume_pending_contexts({13101, 15});
288
289 CASE_EXPECT_TRUE(f.is_ready());
290 CASE_EXPECT_EQ(13116, f.get_internal_promise().data());
291}
292
293static copp::callable_future<int> callable_func_await_int_killed() {
294 generator_future_int_type gen_left_value{[](generator_future_int_type::context_pointer_type ctx) {
295 ++g_suspend_generator_count;
296 g_pending_int_contexts.push_back(ctx);
297 },
298 [](const generator_future_int_type::context_type &ctx) {
299 CASE_EXPECT_FALSE(ctx.is_ready());
300 CASE_EXPECT_TRUE(ctx.is_pending());
301 ++g_resume_generator_count;
302 }};
303
304 // await left value
305 CASE_EXPECT_FALSE(gen_left_value.is_ready());
306 CASE_EXPECT_TRUE(gen_left_value.is_pending());
307 CASE_EXPECT_TRUE(gen_left_value.get_status() == copp::promise_status::kRunning);
308 int x1 = co_await gen_left_value;
309 CASE_EXPECT_FALSE(gen_left_value.is_ready());
310 CASE_EXPECT_TRUE(gen_left_value.is_pending());
311 CASE_EXPECT_TRUE(gen_left_value.get_status() == copp::promise_status::kRunning);
312
313 auto current_callable_status = co_yield copp::callable_future<int>::yield_status();
314 CASE_EXPECT_TRUE(current_callable_status == copp::promise_status::kKilled);
315
316 // All co_await will be ignored after caller is killed
317 co_await gen_left_value;
318
319 generator_future_void_type gen_left_void{[](generator_future_void_type::context_pointer_type ctx) {
320 ++g_suspend_generator_count;
321 g_pending_void_contexts.push_back(ctx);
322 },
323 [](const generator_future_void_type::context_type &ctx) {
324 CASE_EXPECT_FALSE(ctx.is_ready());
325 CASE_EXPECT_TRUE(ctx.is_pending());
326 ++g_resume_generator_count;
327 }};
328
329 // All co_await will be ignored after caller is killed
330 CASE_EXPECT_FALSE(gen_left_void.is_ready());
331 CASE_EXPECT_TRUE(gen_left_void.is_pending());
332 CASE_EXPECT_TRUE(gen_left_void.get_status() == copp::promise_status::kRunning);
333 co_await gen_left_void;
334 CASE_EXPECT_FALSE(gen_left_void.is_ready());
335 CASE_EXPECT_TRUE(gen_left_void.is_pending());
336 CASE_EXPECT_TRUE(gen_left_void.get_status() == copp::promise_status::kRunning);
337
338 co_return x1;
339}
340
341static copp::callable_future<void> callable_func_await_void_killed() {
342 generator_future_void_type gen_left_void{[](generator_future_void_type::context_pointer_type ctx) {
343 ++g_suspend_generator_count;
344 g_pending_void_contexts.push_back(ctx);
345 },
346 [](const generator_future_void_type::context_type &ctx) {
347 CASE_EXPECT_FALSE(ctx.is_ready());
348 CASE_EXPECT_TRUE(ctx.is_pending());
349 ++g_resume_generator_count;
350 }};
351
352 // await left value
353 CASE_EXPECT_FALSE(gen_left_void.is_ready());
354 CASE_EXPECT_TRUE(gen_left_void.is_pending());
355 CASE_EXPECT_TRUE(gen_left_void.get_status() == copp::promise_status::kRunning);
356 co_await gen_left_void;
357 CASE_EXPECT_FALSE(gen_left_void.is_ready());
358 CASE_EXPECT_TRUE(gen_left_void.is_pending());
359 CASE_EXPECT_TRUE(gen_left_void.get_status() == copp::promise_status::kRunning);
360
361 // All co_await will be ignored after caller is killed
362 co_await gen_left_void;
363
364 generator_future_int_type gen_left_value{[](generator_future_int_type::context_pointer_type ctx) {
365 ++g_suspend_generator_count;
366 g_pending_int_contexts.push_back(ctx);
367 },
368 [](const generator_future_int_type::context_type &ctx) {
369 CASE_EXPECT_FALSE(ctx.is_ready());
370 CASE_EXPECT_TRUE(ctx.is_pending());
371 ++g_resume_generator_count;
372 }};
373
374 // All co_await will be ignored after caller is killed
375 CASE_EXPECT_FALSE(gen_left_value.is_ready());
376 CASE_EXPECT_TRUE(gen_left_value.is_pending());
377 CASE_EXPECT_TRUE(gen_left_value.get_status() == copp::promise_status::kRunning);
378 co_await gen_left_value;
379 CASE_EXPECT_FALSE(gen_left_value.is_ready());
380 CASE_EXPECT_TRUE(gen_left_value.is_pending());
381 CASE_EXPECT_TRUE(gen_left_value.get_status() == copp::promise_status::kRunning);
382
383 auto current_callable_status = co_yield copp::callable_future<int>::yield_status();
384 CASE_EXPECT_TRUE(current_callable_status == copp::promise_status::kKilled);
385
386 co_return;
387}
388
389CASE_TEST(generator_promise, caller_killed) {
390 {
391 size_t old_resume_generator_count = g_resume_generator_count;
392 size_t old_suspend_generator_count = g_suspend_generator_count;
393
394 copp::callable_future<int> f = callable_func_await_int_killed();
395
396 // Mock to kill by caller
397 f.kill(copp::promise_status::kKilled, true);
398 CASE_EXPECT_TRUE(f.is_ready());
399
400 CASE_EXPECT_EQ(old_resume_generator_count + 1, g_resume_generator_count);
401 CASE_EXPECT_EQ(old_suspend_generator_count + 1, g_suspend_generator_count);
402 }
403
404 {
405 size_t old_resume_generator_count = g_resume_generator_count;
406 size_t old_suspend_generator_count = g_suspend_generator_count;
407
408 copp::callable_future<void> f = callable_func_await_void_killed();
409
410 // Mock to kill by caller
411 f.kill(copp::promise_status::kKilled, true);
412 CASE_EXPECT_TRUE(f.is_ready());
413
414 CASE_EXPECT_EQ(old_resume_generator_count + 1, g_resume_generator_count);
415 CASE_EXPECT_EQ(old_suspend_generator_count + 1, g_suspend_generator_count);
416 }
417
418 resume_pending_contexts({});
419}
420
421static copp::callable_future<int> callable_func_await_int_killed_by_destroy_generator(
422 std::unique_ptr<generator_future_int_type> &gen_left_value) {
423 gen_left_value.reset(new generator_future_int_type(
424 [](generator_future_int_type::context_pointer_type) {
425 ++g_suspend_generator_count;
426 // No reference to ctx, so it will be destroyed when last generator is
427 // destroyed
428 },
429 [](const generator_future_int_type::context_type &ctx) {
430 CASE_EXPECT_TRUE(ctx.is_ready());
431 CASE_EXPECT_EQ(*ctx.data(), -static_cast<int>(copp::promise_status::kKilled));
432 ++g_resume_generator_count;
433 }));
434
435 // await left value
436 CASE_EXPECT_FALSE(gen_left_value->is_ready());
437 CASE_EXPECT_TRUE(gen_left_value->is_pending());
438 CASE_EXPECT_TRUE(gen_left_value->get_status() == copp::promise_status::kRunning);
439 int x1 = co_await *gen_left_value;
440 CASE_EXPECT_TRUE(nullptr == gen_left_value);
441
442 CASE_EXPECT_EQ(x1, -static_cast<int>(copp::promise_status::kKilled));
443
444 co_return x1;
445}
446
447CASE_TEST(generator_promise, caller_killed_by_destroy_generator) {
448 std::unique_ptr<generator_future_int_type> gen_left_value;
449
450 size_t old_resume_generator_count = g_resume_generator_count;
451 size_t old_suspend_generator_count = g_suspend_generator_count;
452
453 copp::callable_future<int> f = callable_func_await_int_killed_by_destroy_generator(gen_left_value);
454
455 // Mock to kill by caller
456 gen_left_value.reset();
457 CASE_EXPECT_TRUE(f.is_ready());
458
459 CASE_EXPECT_EQ(old_resume_generator_count + 1, g_resume_generator_count);
460 CASE_EXPECT_EQ(old_suspend_generator_count + 1, g_suspend_generator_count);
461}
462
463static copp::callable_future<int> callable_func_await_int_killed_by_destroy_generator_in_callback(
464 std::unique_ptr<generator_future_int_type> &gen_left_value) {
465 gen_left_value.reset(new generator_future_int_type(
466 [&gen_left_value](generator_future_int_type::context_pointer_type) {
467 ++g_suspend_generator_count;
468 // No reference to ctx, so it will be destroyed when last generator is
469 // destroyed
470
471 gen_left_value.reset();
472 },
473 [](const generator_future_int_type::context_type &ctx) {
474 CASE_EXPECT_TRUE(ctx.is_ready());
475 CASE_EXPECT_EQ(*ctx.data(), -static_cast<int>(copp::promise_status::kKilled));
476 ++g_resume_generator_count;
477 }));
478
479 // await left value
480 CASE_EXPECT_FALSE(gen_left_value->is_ready());
481 CASE_EXPECT_TRUE(gen_left_value->is_pending());
482 CASE_EXPECT_TRUE(gen_left_value->get_status() == copp::promise_status::kRunning);
483 int x1 = co_await *gen_left_value;
484 CASE_EXPECT_TRUE(nullptr == gen_left_value);
485
486 CASE_EXPECT_EQ(x1, -static_cast<int>(copp::promise_status::kKilled));
487
488 co_return x1;
489}
490
491CASE_TEST(generator_promise, caller_killed_by_destroy_generator_in_callback) {
492 std::unique_ptr<generator_future_int_type> gen_left_value;
493
494 size_t old_resume_generator_count = g_resume_generator_count;
495 size_t old_suspend_generator_count = g_suspend_generator_count;
496
497 copp::callable_future<int> f = callable_func_await_int_killed_by_destroy_generator_in_callback(gen_left_value);
498
499 // Mock to kill by caller
500 CASE_EXPECT_TRUE(f.is_ready());
501
502 CASE_EXPECT_EQ(old_resume_generator_count + 1, g_resume_generator_count);
503 CASE_EXPECT_EQ(old_suspend_generator_count + 1, g_suspend_generator_count);
504}
505
506class test_context_transform_error_code_type {
507 public:
508 int code;
509
510 test_context_transform_error_code_type() : code(0) {}
511 test_context_transform_error_code_type(int c) : code(c) {}
512 test_context_transform_error_code_type(const test_context_transform_error_code_type &) = default;
513 test_context_transform_error_code_type &operator=(const test_context_transform_error_code_type &) = default;
514 test_context_transform_error_code_type(test_context_transform_error_code_type &&) = default;
515 test_context_transform_error_code_type &operator=(test_context_transform_error_code_type &&) = default;
516 ~test_context_transform_error_code_type() {}
517};
518
519LIBCOPP_COPP_NAMESPACE_BEGIN
520template <>
521struct promise_error_transform<test_context_transform_error_code_type> {
522 using type = test_context_transform_error_code_type;
523 type operator()(promise_status in) const {
524 if (in == promise_status::kTimeout) {
525 return test_context_transform_error_code_type{-500};
526 }
527 return test_context_transform_error_code_type{static_cast<int>(in)};
528 }
529};
530LIBCOPP_COPP_NAMESPACE_END
531
532namespace {
533using test_context_transform_error_code_generator = copp::generator_future<test_context_transform_error_code_type>;
534std::list<test_context_transform_error_code_generator::context_pointer_type>
535 g_test_context_transform_error_code_generator_executor;
536
537static copp::callable_future<int> test_context_transform_error_code_generator_l2() {
538 auto result = co_await test_context_transform_error_code_generator{
539 [](test_context_transform_error_code_generator::context_pointer_type ctx) {
540 g_test_context_transform_error_code_generator_executor.emplace_back(std::move(ctx));
541 }};
542 co_return result.code;
543}
544
545static copp::callable_future<void> test_context_transform_error_code_generator_l1() {
546 auto result = co_await test_context_transform_error_code_generator_l2();
547 CASE_EXPECT_EQ(result, -500);
548 co_return;
549}
550} // namespace
551
552CASE_TEST(generator_promise, transform_error_code) {
553 auto f2 = test_context_transform_error_code_generator_l1();
554 f2.kill(copp::promise_status::kTimeout, true);
555
556 g_test_context_transform_error_code_generator_executor.clear();
557}
558
559static copp::callable_future<int> callable_func_multiple_await_int(generator_future_int_type &shared_generator) {
560 auto result = co_await shared_generator;
561 co_return result;
562}
563
564CASE_TEST(generator_promise, miltiple_wait_int_generator) {
565 size_t old_resume_generator_count = g_resume_generator_count;
566 size_t old_suspend_generator_count = g_suspend_generator_count;
567
568 generator_future_int_type int_generator{
569 [](generator_future_int_type::context_pointer_type ctx) {
570 ++g_suspend_generator_count;
571 if (g_pending_int_contexts.end() ==
572 std::find(g_pending_int_contexts.begin(), g_pending_int_contexts.end(), ctx)) {
573 g_pending_int_contexts.push_back(ctx);
574 }
575 },
576 [](const generator_future_int_type::context_type &) { ++g_resume_generator_count; }};
577
578 copp::callable_future<int> f1 = callable_func_multiple_await_int(int_generator);
579 copp::callable_future<int> f2 = callable_func_multiple_await_int(int_generator);
580
581 CASE_EXPECT_NE(static_cast<int>(copp::promise_status::kDone), static_cast<int>(f1.get_status()));
582 CASE_EXPECT_FALSE(f1.is_ready());
583 CASE_EXPECT_NE(static_cast<int>(copp::promise_status::kDone), static_cast<int>(f2.get_status()));
584 CASE_EXPECT_FALSE(f2.is_ready());
585
586 CASE_EXPECT_EQ(1, resume_pending_contexts({143}));
587
588 CASE_EXPECT_TRUE(f1.is_ready());
589 CASE_EXPECT_EQ(143, f1.get_internal_promise().data());
590 CASE_EXPECT_TRUE(f2.is_ready());
591 CASE_EXPECT_EQ(143, f2.get_internal_promise().data());
592
593 CASE_EXPECT_EQ(old_resume_generator_count + 2, g_resume_generator_count);
594 CASE_EXPECT_EQ(old_suspend_generator_count + 2, g_suspend_generator_count);
595}
596
597static copp::callable_future<int> callable_func_resume_when_suspend() {
598 auto result = co_await generator_future_int_type{
599 [](generator_future_int_type::context_pointer_type ctx) {
600 ++g_suspend_generator_count;
601 ctx->set_value(369);
602 },
603 [](const generator_future_int_type::context_type &) { ++g_resume_generator_count; }};
604 co_return result;
605}
606
607CASE_TEST(generator_promise, resume_when_suspend) {
608 size_t old_resume_generator_count = g_resume_generator_count;
609 size_t old_suspend_generator_count = g_suspend_generator_count;
610
611 copp::callable_future<int> f1 = callable_func_resume_when_suspend();
612
613 CASE_EXPECT_EQ(static_cast<int>(copp::promise_status::kDone), static_cast<int>(f1.get_status()));
614 CASE_EXPECT_TRUE(f1.is_ready());
615 CASE_EXPECT_EQ(369, f1.get_internal_promise().data());
616
617 CASE_EXPECT_EQ(old_resume_generator_count + 1, g_resume_generator_count);
618 CASE_EXPECT_EQ(old_suspend_generator_count + 1, g_suspend_generator_count);
619}
620
621static copp::callable_future<int> callable_func_some_generator_in_container(size_t expect_ready_count,
622 copp::promise_status expect_status) {
623 size_t old_resume_generator_count = g_resume_generator_count;
624 size_t old_suspend_generator_count = g_suspend_generator_count;
625
626 size_t resume_ready_count = 0;
627 auto suspend_callback = [](generator_future_int_type::context_pointer_type ctx) {
628 ++g_suspend_generator_count;
629 g_pending_int_contexts.push_back(ctx);
630 };
631 auto resume_callback = [&resume_ready_count](const generator_future_int_type::context_type &ctx) {
632 ++g_resume_generator_count;
633 if (ctx.is_ready()) {
634 ++resume_ready_count;
635 }
636 };
637
638 std::vector<generator_future_int_type> generators;
639 generators.push_back(generator_future_int_type(suspend_callback, resume_callback));
640 generators.push_back(generator_future_int_type(suspend_callback, resume_callback));
641 generators.push_back(generator_future_int_type(suspend_callback, resume_callback));
642
643 copp::some_ready<generator_future_int_type>::type readys;
644 auto some_result = co_await copp::some(readys, 2, generators);
645 CASE_EXPECT_EQ(static_cast<int>(expect_status), static_cast<int>(some_result));
646
647 int result = 1;
648 for (auto &ready_generator : readys) {
649 result += *ready_generator->get_context()->data();
650 }
651
652 CASE_EXPECT_EQ(old_resume_generator_count + 3, g_resume_generator_count);
653 CASE_EXPECT_EQ(old_suspend_generator_count + 3, g_suspend_generator_count);
654 CASE_EXPECT_EQ(expect_ready_count, resume_ready_count);
655
656 // Nothing happend here if we await the generators again.
657 some_result = co_await copp::some(readys, 2, generators);
658 CASE_EXPECT_EQ(static_cast<int>(expect_status), static_cast<int>(some_result));
659
660 // If it's killed, await will trigger suspend and resume again, or it will return directly.
661 if (expect_status > copp::promise_status::kDone) {
662 CASE_EXPECT_EQ(old_resume_generator_count + 6 - resume_ready_count, g_resume_generator_count);
663 CASE_EXPECT_EQ(old_suspend_generator_count + 6 - resume_ready_count, g_suspend_generator_count);
664 CASE_EXPECT_EQ(expect_ready_count, resume_ready_count);
665 } else {
666 CASE_EXPECT_EQ(old_resume_generator_count + 3, g_resume_generator_count);
667 CASE_EXPECT_EQ(old_suspend_generator_count + 3, g_suspend_generator_count);
668 CASE_EXPECT_EQ(expect_ready_count, resume_ready_count);
669 }
670
671 co_return result;
672}
673
674CASE_TEST(generator_promise, finish_some_in_container) {
675 auto f = callable_func_some_generator_in_container(2, copp::promise_status::kDone);
676
677 CASE_EXPECT_FALSE(f.is_ready());
678
679 // partly resume
680 resume_pending_contexts({471}, 1);
681 CASE_EXPECT_FALSE(f.is_ready());
682 resume_pending_contexts({473}, 1);
683
684 CASE_EXPECT_TRUE(f.is_ready());
685 CASE_EXPECT_EQ(945, f.get_internal_promise().data());
686
687 resume_pending_contexts({});
688}
689
690CASE_TEST(generator_promise, kill_some_in_container) {
691 auto f = callable_func_some_generator_in_container(0, copp::promise_status::kKilled);
692
693 CASE_EXPECT_FALSE(f.is_ready());
694
695 // partly resume
696 f.kill();
697
698 CASE_EXPECT_TRUE(f.is_ready());
699 CASE_EXPECT_EQ(1, f.get_internal_promise().data());
700
701 resume_pending_contexts({});
702}
703
704static copp::callable_future<int> callable_func_some_generator_in_initialize_list(size_t expect_ready_count,
705 copp::promise_status expect_status) {
706 size_t old_resume_generator_count = g_resume_generator_count;
707 size_t old_suspend_generator_count = g_suspend_generator_count;
708
709 size_t resume_ready_count = 0;
710 auto suspend_callback = [](generator_future_int_type::context_pointer_type ctx) {
711 ++g_suspend_generator_count;
712 g_pending_int_contexts.push_back(ctx);
713 };
714 auto resume_callback = [&resume_ready_count](const generator_future_int_type::context_type &ctx) {
715 ++g_resume_generator_count;
716 if (ctx.is_ready()) {
717 ++resume_ready_count;
718 }
719 };
720
721 generator_future_int_type gen1{suspend_callback, resume_callback};
722 generator_future_int_type gen2{suspend_callback, resume_callback};
723 generator_future_int_type gen3{suspend_callback, resume_callback};
724
725 copp::some_ready<generator_future_int_type>::type readys;
726 std::array<std::reference_wrapper<generator_future_int_type>, 3> pending = {gen1, gen2, gen3};
727 auto some_result = co_await copp::some(readys, 2, copp::gsl::make_span(pending));
728 CASE_EXPECT_EQ(static_cast<int>(expect_status), static_cast<int>(some_result));
729
730 int result = 1;
731 for (auto &ready_generator : readys) {
732 result += *ready_generator->get_context()->data();
733 }
734
735 CASE_EXPECT_EQ(old_resume_generator_count + 3, g_resume_generator_count);
736 CASE_EXPECT_EQ(old_suspend_generator_count + 3, g_suspend_generator_count);
737 CASE_EXPECT_EQ(expect_ready_count, resume_ready_count);
738
739 co_return result;
740}
741
742CASE_TEST(generator_promise, finish_some_in_initialize_list) {
743 auto f = callable_func_some_generator_in_initialize_list(2, copp::promise_status::kDone);
744
745 CASE_EXPECT_FALSE(f.is_ready());
746
747 // partly resume
748 resume_pending_contexts({471}, 1);
749 CASE_EXPECT_FALSE(f.is_ready());
750 resume_pending_contexts({473}, 1);
751
752 CASE_EXPECT_TRUE(f.is_ready());
753 CASE_EXPECT_EQ(945, f.get_internal_promise().data());
754
755 resume_pending_contexts({});
756}
757
758static copp::callable_future<int> callable_func_any_generator_in_container(size_t expect_ready_count,
759 copp::promise_status expect_status) {
760 size_t old_resume_generator_count = g_resume_generator_count;
761 size_t old_suspend_generator_count = g_suspend_generator_count;
762
763 size_t resume_ready_count = 0;
764 auto suspend_callback = [](generator_future_int_type::context_pointer_type ctx) {
765 ++g_suspend_generator_count;
766 g_pending_int_contexts.push_back(ctx);
767 };
768 auto resume_callback = [&resume_ready_count](const generator_future_int_type::context_type &ctx) {
769 ++g_resume_generator_count;
770 if (ctx.is_ready()) {
771 ++resume_ready_count;
772 }
773 };
774
775 std::vector<generator_future_int_type> generators;
776 generators.push_back(generator_future_int_type(suspend_callback, resume_callback));
777 generators.push_back(generator_future_int_type(suspend_callback, resume_callback));
778 generators.push_back(generator_future_int_type(suspend_callback, resume_callback));
779
780 copp::any_ready<generator_future_int_type>::type readys;
781 auto any_result = co_await copp::any(readys, generators);
782 CASE_EXPECT_EQ(static_cast<int>(expect_status), static_cast<int>(any_result));
783
784 int result = 1;
785 for (auto &ready_generator : readys) {
786 result += *ready_generator->get_context()->data();
787 }
788
789 CASE_EXPECT_EQ(old_resume_generator_count + 3, g_resume_generator_count);
790 CASE_EXPECT_EQ(old_suspend_generator_count + 3, g_suspend_generator_count);
791 CASE_EXPECT_EQ(expect_ready_count, resume_ready_count);
792
793 // Nothing happend here if we await the generators again.
794 std::vector<copp::gsl::not_null<generator_future_int_type *>> generators_not_null_type;
795 for (auto &generator : generators) {
796 generators_not_null_type.push_back(copp::gsl::make_not_null(&generator));
797 }
798 any_result = co_await copp::any(readys, generators_not_null_type);
799 CASE_EXPECT_EQ(static_cast<int>(expect_status), static_cast<int>(any_result));
800
801 // If it's killed, await will trigger suspend and resume again, or it will return directly.
802 if (expect_status > copp::promise_status::kDone) {
803 CASE_EXPECT_EQ(old_resume_generator_count + 6 - resume_ready_count, g_resume_generator_count);
804 CASE_EXPECT_EQ(old_suspend_generator_count + 6 - resume_ready_count, g_suspend_generator_count);
805 CASE_EXPECT_EQ(expect_ready_count, resume_ready_count);
806 } else {
807 CASE_EXPECT_EQ(old_resume_generator_count + 3, g_resume_generator_count);
808 CASE_EXPECT_EQ(old_suspend_generator_count + 3, g_suspend_generator_count);
809 CASE_EXPECT_EQ(expect_ready_count, resume_ready_count);
810 }
811
812 co_return result;
813}
814
815CASE_TEST(generator_promise, finish_any_in_container) {
816 auto f = callable_func_any_generator_in_container(1, copp::promise_status::kDone);
817
818 CASE_EXPECT_FALSE(f.is_ready());
819
820 // partly resume
821 resume_pending_contexts({671}, 1);
822
823 CASE_EXPECT_TRUE(f.is_ready());
824 CASE_EXPECT_EQ(672, f.get_internal_promise().data());
825
826 resume_pending_contexts({});
827}
828
829static copp::callable_future<int> callable_func_all_generator_in_container(size_t expect_ready_count,
830 copp::promise_status expect_status) {
831 size_t old_resume_generator_count = g_resume_generator_count;
832 size_t old_suspend_generator_count = g_suspend_generator_count;
833
834 size_t resume_ready_count = 0;
835 auto suspend_callback = [](generator_future_int_type::context_pointer_type ctx) {
836 ++g_suspend_generator_count;
837 g_pending_int_contexts.push_back(ctx);
838 };
839 auto resume_callback = [&resume_ready_count](const generator_future_int_type::context_type &ctx) {
840 ++g_resume_generator_count;
841 if (ctx.is_ready()) {
842 ++resume_ready_count;
843 }
844 };
845
846 std::vector<generator_future_int_type> generators;
847 generators.push_back(generator_future_int_type(suspend_callback, resume_callback));
848 generators.push_back(generator_future_int_type(suspend_callback, resume_callback));
849 generators.push_back(generator_future_int_type(suspend_callback, resume_callback));
850
851 copp::all_ready<generator_future_int_type>::type readys;
852 auto all_result = co_await copp::all(readys, generators);
853 CASE_EXPECT_EQ(static_cast<int>(expect_status), static_cast<int>(all_result));
854
855 int result = 1;
856 for (auto &ready_generator : readys) {
857 result += *ready_generator->get_context()->data();
858 }
859
860 CASE_EXPECT_EQ(old_resume_generator_count + 3, g_resume_generator_count);
861 CASE_EXPECT_EQ(old_suspend_generator_count + 3, g_suspend_generator_count);
862 CASE_EXPECT_EQ(expect_ready_count, resume_ready_count);
863
864 // Nothing happend here if we await the generators again.
865 all_result = co_await copp::all(readys, generators);
866 CASE_EXPECT_EQ(static_cast<int>(expect_status), static_cast<int>(all_result));
867
868 // If it's killed, await will trigger suspend and resume again, or it will return directly.
869 if (expect_status > copp::promise_status::kDone) {
870 CASE_EXPECT_EQ(old_resume_generator_count + 6 - resume_ready_count, g_resume_generator_count);
871 CASE_EXPECT_EQ(old_suspend_generator_count + 6 - resume_ready_count, g_suspend_generator_count);
872 CASE_EXPECT_EQ(expect_ready_count, resume_ready_count);
873 } else {
874 CASE_EXPECT_EQ(old_resume_generator_count + 3, g_resume_generator_count);
875 CASE_EXPECT_EQ(old_suspend_generator_count + 3, g_suspend_generator_count);
876 CASE_EXPECT_EQ(expect_ready_count, resume_ready_count);
877 }
878
879 co_return result;
880}
881
882CASE_TEST(generator_promise, finish_all_in_container) {
883 auto f = callable_func_all_generator_in_container(3, copp::promise_status::kDone);
884
885 CASE_EXPECT_FALSE(f.is_ready());
886
887 // partly resume
888 resume_pending_contexts({671}, 1);
889 CASE_EXPECT_FALSE(f.is_ready());
890
891 resume_pending_contexts({791, 793}, 2);
892
893 CASE_EXPECT_TRUE(f.is_ready());
894 CASE_EXPECT_EQ(2256, f.get_internal_promise().data());
895
896 resume_pending_contexts({});
897}
898
899#else
900CASE_TEST(generator_promise, disabled) {}
901#endif
#define CASE_EXPECT_FALSE(c)
Definition test_macros.h:98
#define CASE_EXPECT_EQ(l, r)
Definition test_macros.h:99
#define CASE_EXPECT_NE(l, r)
#define CASE_TEST(test_name, case_name)
Definition test_macros.h:47
#define CASE_EXPECT_TRUE(c)
Definition test_macros.h:97