libcopp 2.3.2
Loading...
Searching...
No Matches
coroutine_context_channel_test.cpp
Go to the documentation of this file.
1// Copyright 2026 owent
2
6
7#include <cstdio>
8#include <cstring>
9#include <iostream>
10#include <vector>
11
12#include "frame/test_macros.h"
13
15 copp::coroutine_context_container<copp::allocator::stack_allocator_memory>;
16
17#if defined(LIBCOPP_MACRO_ENABLE_WIN_FIBER) && LIBCOPP_MACRO_ENABLE_WIN_FIBER
18using test_context_channel_coroutine_context_fiber_test_type =
19 copp::coroutine_context_fiber_container<copp::allocator::stack_allocator_memory>;
20#endif
21
23
24template <class T>
26
27template <>
29 int operator()(copp::copp_error_code err_code) { return static_cast<int>(err_code) - 10000; }
30};
31
32template <>
34 int *operator()(copp::copp_error_code /*err_code*/) { return &g_test_coroutine_channel_status; }
35};
36
37template <class TCTX>
39
40template <>
43 return copp::this_coroutine::get<test_context_channel_coroutine_context_test_type>();
44 }
45};
46
47#if defined(LIBCOPP_MACRO_ENABLE_WIN_FIBER) && LIBCOPP_MACRO_ENABLE_WIN_FIBER
48template <>
49struct test_context_channel_this_coroutine<test_context_channel_coroutine_context_fiber_test_type> {
50 static test_context_channel_coroutine_context_fiber_test_type *get() {
51 return copp::this_fiber::get<test_context_channel_coroutine_context_fiber_test_type>();
52 }
53};
54#endif
55
56template <class TCTX, class T>
58 public:
60
63 copp::memory::strong_rc_ptr<copp::stackful_channel_sender<T>> sender;
65
66 copp::stackful_channel_receiver<T> create_channel() {
67 auto ret = copp::make_stackful_channel<T>();
68 sender = copp::memory::make_strong_rc<copp::stackful_channel_sender<T>>(ret.second);
69 return std::move(ret.first);
70 }
71
86};
87
88CASE_TEST(coroutine_channel, channel_int) {
89 unsigned char *stack_buff = new unsigned char[128 * 1024];
90
92 // Normal
95 {
96 copp::stack_context test_move_alloc;
97
98 copp::allocator::stack_allocator_memory alloc_created(stack_buff, 128 * 1024);
99 copp::allocator::stack_allocator_memory alloc(alloc_created);
100
101 alloc_created.allocate(test_move_alloc, 64 * 1024);
102 CASE_EXPECT_EQ(nullptr, test_move_alloc.sp);
103
104 test_context_channel_coroutine_context_test_type::ptr_t co =
105 test_context_channel_coroutine_context_test_type::create(&runner, alloc);
106 runner.call_times = 0;
107
108 CASE_EXPECT_TRUE(!!co);
109
110 co->start();
111
113
114 runner.sender->set_value(137);
115
117 CASE_EXPECT_EQ(runner.last_value, 137);
118
119 CASE_EXPECT_EQ(LIBCOPP_COPP_NAMESPACE_ID::COPP_EC_NOT_READY, co->resume());
120 CASE_EXPECT_EQ(LIBCOPP_COPP_NAMESPACE_ID::COPP_EC_ALREADY_EXIST, co->yield());
121 }
122
123 // auto transform
126 {
127 copp::stack_context test_move_alloc;
128
129 copp::allocator::stack_allocator_memory alloc_created(stack_buff, 128 * 1024);
130 copp::allocator::stack_allocator_memory alloc(alloc_created);
131
132 alloc_created.allocate(test_move_alloc, 64 * 1024);
133 CASE_EXPECT_EQ(nullptr, test_move_alloc.sp);
134
135 test_context_channel_coroutine_context_test_type::ptr_t co =
136 test_context_channel_coroutine_context_test_type::create(&runner, alloc);
137 runner.call_times = 0;
138
139 CASE_EXPECT_TRUE(!!co);
140
141 co->start();
142
144
145 co->resume(nullptr);
146
148 CASE_EXPECT_EQ(runner.last_value, copp::COPP_EC_OPERATION_CANCLE);
149
150 CASE_EXPECT_EQ(LIBCOPP_COPP_NAMESPACE_ID::COPP_EC_NOT_READY, co->resume());
151 CASE_EXPECT_EQ(LIBCOPP_COPP_NAMESPACE_ID::COPP_EC_ALREADY_EXIST, co->yield());
152 }
153
154 // custom transform
157 {
158 copp::stack_context test_move_alloc;
159
160 copp::allocator::stack_allocator_memory alloc_created(stack_buff, 128 * 1024);
161 copp::allocator::stack_allocator_memory alloc(alloc_created);
162
163 alloc_created.allocate(test_move_alloc, 64 * 1024);
164 CASE_EXPECT_EQ(nullptr, test_move_alloc.sp);
165
166 test_context_channel_coroutine_context_test_type::ptr_t co =
167 test_context_channel_coroutine_context_test_type::create(&runner, alloc);
168 runner.call_times = 0;
169 runner.use_error_transform = true;
170
171 CASE_EXPECT_TRUE(!!co);
172
173 co->start();
174
176
177 co->resume(nullptr);
178
180 CASE_EXPECT_EQ(runner.last_value, copp::COPP_EC_OPERATION_CANCLE - 10000);
181
182 CASE_EXPECT_EQ(LIBCOPP_COPP_NAMESPACE_ID::COPP_EC_NOT_READY, co->resume());
183 CASE_EXPECT_EQ(LIBCOPP_COPP_NAMESPACE_ID::COPP_EC_ALREADY_EXIST, co->yield());
184 }
185
186 delete[] stack_buff;
187}
188
189CASE_TEST(coroutine_channel, channel_pointer) {
190 unsigned char *stack_buff = new unsigned char[128 * 1024];
191
193 // Normal
196 {
197 copp::stack_context test_move_alloc;
198
199 copp::allocator::stack_allocator_memory alloc_created(stack_buff, 128 * 1024);
200 copp::allocator::stack_allocator_memory alloc(alloc_created);
201
202 alloc_created.allocate(test_move_alloc, 64 * 1024);
203 CASE_EXPECT_EQ(nullptr, test_move_alloc.sp);
204
205 test_context_channel_coroutine_context_test_type::ptr_t co =
206 test_context_channel_coroutine_context_test_type::create(&runner, alloc);
207 runner.call_times = 0;
208
209 CASE_EXPECT_TRUE(!!co);
210
211 co->start();
212
214
215 runner.sender->set_value(reinterpret_cast<int *>(stack_buff));
216
218 CASE_EXPECT_EQ(runner.last_value, reinterpret_cast<int *>(stack_buff));
219
220 CASE_EXPECT_EQ(LIBCOPP_COPP_NAMESPACE_ID::COPP_EC_NOT_READY, co->resume());
221 CASE_EXPECT_EQ(LIBCOPP_COPP_NAMESPACE_ID::COPP_EC_ALREADY_EXIST, co->yield());
222 }
223
224 // auto transform
227 {
228 copp::stack_context test_move_alloc;
229
230 copp::allocator::stack_allocator_memory alloc_created(stack_buff, 128 * 1024);
231 copp::allocator::stack_allocator_memory alloc(alloc_created);
232
233 alloc_created.allocate(test_move_alloc, 64 * 1024);
234 CASE_EXPECT_EQ(nullptr, test_move_alloc.sp);
235
236 test_context_channel_coroutine_context_test_type::ptr_t co =
237 test_context_channel_coroutine_context_test_type::create(&runner, alloc);
238 runner.call_times = 0;
239
240 CASE_EXPECT_TRUE(!!co);
241
242 co->start();
243
245
246 co->resume(nullptr);
247
249 CASE_EXPECT_EQ(runner.last_value, nullptr);
250
251 CASE_EXPECT_EQ(LIBCOPP_COPP_NAMESPACE_ID::COPP_EC_NOT_READY, co->resume());
252 CASE_EXPECT_EQ(LIBCOPP_COPP_NAMESPACE_ID::COPP_EC_ALREADY_EXIST, co->yield());
253 }
254
255 // custom transform
258 {
259 copp::stack_context test_move_alloc;
260
261 copp::allocator::stack_allocator_memory alloc_created(stack_buff, 128 * 1024);
262 copp::allocator::stack_allocator_memory alloc(alloc_created);
263
264 alloc_created.allocate(test_move_alloc, 64 * 1024);
265 CASE_EXPECT_EQ(nullptr, test_move_alloc.sp);
266
267 test_context_channel_coroutine_context_test_type::ptr_t co =
268 test_context_channel_coroutine_context_test_type::create(&runner, alloc);
269 runner.call_times = 0;
270 runner.use_error_transform = true;
271
272 CASE_EXPECT_TRUE(!!co);
273
274 co->start();
275
277
278 co->resume(nullptr);
279
282
283 CASE_EXPECT_EQ(LIBCOPP_COPP_NAMESPACE_ID::COPP_EC_NOT_READY, co->resume());
284 CASE_EXPECT_EQ(LIBCOPP_COPP_NAMESPACE_ID::COPP_EC_ALREADY_EXIST, co->yield());
285 }
286
287 delete[] stack_buff;
288}
289
290#if defined(LIBCOPP_MACRO_ENABLE_WIN_FIBER) && LIBCOPP_MACRO_ENABLE_WIN_FIBER
291
292CASE_TEST(coroutine_channel, fiber_channel_int) {
293 unsigned char *stack_buff = new unsigned char[128 * 1024];
294
296 // Normal
299 {
300 copp::stack_context test_move_alloc;
301
302 copp::allocator::stack_allocator_memory alloc_created(stack_buff, 128 * 1024);
303 copp::allocator::stack_allocator_memory alloc(alloc_created);
304
305 alloc_created.allocate(test_move_alloc, 64 * 1024);
306 CASE_EXPECT_EQ(nullptr, test_move_alloc.sp);
307
308 test_context_channel_coroutine_context_fiber_test_type::ptr_t co =
309 test_context_channel_coroutine_context_fiber_test_type::create(&runner, alloc);
310 runner.call_times = 0;
311
312 CASE_EXPECT_TRUE(!!co);
313
314 co->start();
315
317
318 runner.sender->set_value(157);
319
321 CASE_EXPECT_EQ(runner.last_value, 157);
322
323 CASE_EXPECT_EQ(LIBCOPP_COPP_NAMESPACE_ID::COPP_EC_NOT_READY, co->resume());
324 CASE_EXPECT_EQ(LIBCOPP_COPP_NAMESPACE_ID::COPP_EC_ALREADY_EXIST, co->yield());
325 }
326
327 // auto transform
330 {
331 copp::stack_context test_move_alloc;
332
333 copp::allocator::stack_allocator_memory alloc_created(stack_buff, 128 * 1024);
334 copp::allocator::stack_allocator_memory alloc(alloc_created);
335
336 alloc_created.allocate(test_move_alloc, 64 * 1024);
337 CASE_EXPECT_EQ(nullptr, test_move_alloc.sp);
338
339 test_context_channel_coroutine_context_fiber_test_type::ptr_t co =
340 test_context_channel_coroutine_context_fiber_test_type::create(&runner, alloc);
341 runner.call_times = 0;
342
343 CASE_EXPECT_TRUE(!!co);
344
345 co->start();
346
348
349 co->resume(nullptr);
350
352 CASE_EXPECT_EQ(runner.last_value, copp::COPP_EC_OPERATION_CANCLE);
353
354 CASE_EXPECT_EQ(LIBCOPP_COPP_NAMESPACE_ID::COPP_EC_NOT_READY, co->resume());
355 CASE_EXPECT_EQ(LIBCOPP_COPP_NAMESPACE_ID::COPP_EC_ALREADY_EXIST, co->yield());
356 }
357
358 // custom transform
361 {
362 copp::stack_context test_move_alloc;
363
364 copp::allocator::stack_allocator_memory alloc_created(stack_buff, 128 * 1024);
365 copp::allocator::stack_allocator_memory alloc(alloc_created);
366
367 alloc_created.allocate(test_move_alloc, 64 * 1024);
368 CASE_EXPECT_EQ(nullptr, test_move_alloc.sp);
369
370 test_context_channel_coroutine_context_fiber_test_type::ptr_t co =
371 test_context_channel_coroutine_context_fiber_test_type::create(&runner, alloc);
372 runner.call_times = 0;
373 runner.use_error_transform = true;
374
375 CASE_EXPECT_TRUE(!!co);
376
377 co->start();
378
380
381 co->resume(nullptr);
382
384 CASE_EXPECT_EQ(runner.last_value, copp::COPP_EC_OPERATION_CANCLE - 10000);
385
386 CASE_EXPECT_EQ(LIBCOPP_COPP_NAMESPACE_ID::COPP_EC_NOT_READY, co->resume());
387 CASE_EXPECT_EQ(LIBCOPP_COPP_NAMESPACE_ID::COPP_EC_ALREADY_EXIST, co->yield());
388 }
389
390 delete[] stack_buff;
391}
392
393CASE_TEST(coroutine_channel, fiber_channel_pointer) {
394 unsigned char *stack_buff = new unsigned char[128 * 1024];
395
397 // Normal
400 {
401 copp::stack_context test_move_alloc;
402
403 copp::allocator::stack_allocator_memory alloc_created(stack_buff, 128 * 1024);
404 copp::allocator::stack_allocator_memory alloc(alloc_created);
405
406 alloc_created.allocate(test_move_alloc, 64 * 1024);
407 CASE_EXPECT_EQ(nullptr, test_move_alloc.sp);
408
409 test_context_channel_coroutine_context_fiber_test_type::ptr_t co =
410 test_context_channel_coroutine_context_fiber_test_type::create(&runner, alloc);
411 runner.call_times = 0;
412
413 CASE_EXPECT_TRUE(!!co);
414
415 co->start();
416
418
419 runner.sender->set_value(reinterpret_cast<int *>(stack_buff));
420
422 CASE_EXPECT_EQ(runner.last_value, reinterpret_cast<int *>(stack_buff));
423
424 CASE_EXPECT_EQ(LIBCOPP_COPP_NAMESPACE_ID::COPP_EC_NOT_READY, co->resume());
425 CASE_EXPECT_EQ(LIBCOPP_COPP_NAMESPACE_ID::COPP_EC_ALREADY_EXIST, co->yield());
426 }
427
428 // auto transform
431 {
432 copp::stack_context test_move_alloc;
433
434 copp::allocator::stack_allocator_memory alloc_created(stack_buff, 128 * 1024);
435 copp::allocator::stack_allocator_memory alloc(alloc_created);
436
437 alloc_created.allocate(test_move_alloc, 64 * 1024);
438 CASE_EXPECT_EQ(nullptr, test_move_alloc.sp);
439
440 test_context_channel_coroutine_context_fiber_test_type::ptr_t co =
441 test_context_channel_coroutine_context_fiber_test_type::create(&runner, alloc);
442 runner.call_times = 0;
443
444 CASE_EXPECT_TRUE(!!co);
445
446 co->start();
447
449
450 co->resume(nullptr);
451
453 CASE_EXPECT_EQ(runner.last_value, nullptr);
454
455 CASE_EXPECT_EQ(LIBCOPP_COPP_NAMESPACE_ID::COPP_EC_NOT_READY, co->resume());
456 CASE_EXPECT_EQ(LIBCOPP_COPP_NAMESPACE_ID::COPP_EC_ALREADY_EXIST, co->yield());
457 }
458
459 // custom transform
462 {
463 copp::stack_context test_move_alloc;
464
465 copp::allocator::stack_allocator_memory alloc_created(stack_buff, 128 * 1024);
466 copp::allocator::stack_allocator_memory alloc(alloc_created);
467
468 alloc_created.allocate(test_move_alloc, 64 * 1024);
469 CASE_EXPECT_EQ(nullptr, test_move_alloc.sp);
470
471 test_context_channel_coroutine_context_fiber_test_type::ptr_t co =
472 test_context_channel_coroutine_context_fiber_test_type::create(&runner, alloc);
473 runner.call_times = 0;
474 runner.use_error_transform = true;
475
476 CASE_EXPECT_TRUE(!!co);
477
478 co->start();
479
481
482 co->resume(nullptr);
483
486
487 CASE_EXPECT_EQ(LIBCOPP_COPP_NAMESPACE_ID::COPP_EC_NOT_READY, co->resume());
488 CASE_EXPECT_EQ(LIBCOPP_COPP_NAMESPACE_ID::COPP_EC_ALREADY_EXIST, co->yield());
489 }
490
491 delete[] stack_buff;
492}
493#endif
494
495namespace {
496struct test_context_channel_fifo_context : public copp::stackful_channel_context_base {};
497
498std::vector<int> g_test_coroutine_channel_fifo_order;
499
500int test_context_channel_fifo_resume(void *handle_data, copp::stackful_channel_context_base *) {
501 g_test_coroutine_channel_fifo_order.push_back(*reinterpret_cast<int *>(handle_data));
502 return 0;
503}
504} // namespace
505
506CASE_TEST(coroutine_channel, multiple_callers_resume_fifo) {
507 constexpr int k_caller_count = 8;
508 int caller_indexes[k_caller_count];
509 copp::stackful_channel_handle_delegate callers[k_caller_count];
510 test_context_channel_fifo_context context;
511
512 g_test_coroutine_channel_fifo_order.clear();
513 for (int index = 0; index < k_caller_count; ++index) {
514 caller_indexes[index] = index;
515 callers[index].handle_data = &caller_indexes[index];
516 callers[index].resume_handle = &test_context_channel_fifo_resume;
517 context.add_caller(callers[index]);
518 }
519 // A repeated registration of the same handle is ignored.
520 context.add_caller(callers[3]);
521
522 CASE_EXPECT_TRUE(context.has_multiple_callers());
523 CASE_EXPECT_EQ(k_caller_count, static_cast<int>(context.resume_callers()));
524 CASE_EXPECT_EQ(k_caller_count, static_cast<int>(g_test_coroutine_channel_fifo_order.size()));
525 for (int index = 0; index < k_caller_count; ++index) {
526 CASE_EXPECT_EQ(index, g_test_coroutine_channel_fifo_order[static_cast<size_t>(index)]);
527 }
528
529 // A handle removed and registered again joins the back of the queue.
530 g_test_coroutine_channel_fifo_order.clear();
531 for (int index = 0; index < k_caller_count; ++index) {
532 context.add_caller(callers[index]);
533 }
534 CASE_EXPECT_TRUE(context.remove_caller(callers[2]));
535 context.add_caller(callers[2]);
536
537 const int expected_order[k_caller_count] = {0, 1, 3, 4, 5, 6, 7, 2};
538 CASE_EXPECT_EQ(k_caller_count, static_cast<int>(context.resume_callers()));
539 CASE_EXPECT_EQ(k_caller_count, static_cast<int>(g_test_coroutine_channel_fifo_order.size()));
540 if (k_caller_count == static_cast<int>(g_test_coroutine_channel_fifo_order.size())) {
541 for (int index = 0; index < k_caller_count; ++index) {
542 CASE_EXPECT_EQ(expected_order[index], g_test_coroutine_channel_fifo_order[static_cast<size_t>(index)]);
543 }
544 }
545}
546
547CASE_TEST(coroutine_channel, multiple_callers_resume_fifo_after_first_removed) {
548 int caller_indexes[3] = {0, 1, 2};
549 copp::stackful_channel_handle_delegate callers[3];
550 test_context_channel_fifo_context context;
551 for (int index = 0; index < 3; ++index) {
552 callers[index].handle_data = &caller_indexes[index];
553 callers[index].resume_handle = &test_context_channel_fifo_resume;
554 }
555
556 context.add_caller(callers[0]);
557 context.add_caller(callers[1]);
558 CASE_EXPECT_TRUE(context.remove_caller(callers[0]));
559 CASE_EXPECT_FALSE(context.has_multiple_callers());
560
561 // New and re-registered callers must stay behind the remaining waiter.
562 context.add_caller(callers[2]);
563 context.add_caller(callers[0]);
564 context.add_caller(callers[1]);
565 CASE_EXPECT_TRUE(context.has_multiple_callers());
566
567 g_test_coroutine_channel_fifo_order.clear();
568 CASE_EXPECT_EQ(3, static_cast<int>(context.resume_callers()));
569 CASE_EXPECT_EQ(3, static_cast<int>(g_test_coroutine_channel_fifo_order.size()));
570 if (g_test_coroutine_channel_fifo_order.size() == 3) {
571 CASE_EXPECT_EQ(1, g_test_coroutine_channel_fifo_order[0]);
572 CASE_EXPECT_EQ(2, g_test_coroutine_channel_fifo_order[1]);
573 CASE_EXPECT_EQ(0, g_test_coroutine_channel_fifo_order[2]);
574 }
575 CASE_EXPECT_FALSE(context.has_multiple_callers());
576 CASE_EXPECT_EQ(0, static_cast<int>(context.resume_callers()));
577}
578
579CASE_TEST(coroutine_channel, multiple_callers_add_remove_edge_cases) {
580 int caller_indexes[3] = {0, 1, 2};
581 copp::stackful_channel_handle_delegate first_caller;
582 copp::stackful_channel_handle_delegate second_caller;
583 copp::stackful_channel_handle_delegate unregistered_caller;
584 first_caller.handle_data = &caller_indexes[0];
585 first_caller.resume_handle = &test_context_channel_fifo_resume;
586 second_caller.handle_data = &caller_indexes[1];
587 second_caller.resume_handle = &test_context_channel_fifo_resume;
588 unregistered_caller.handle_data = &caller_indexes[2];
589 unregistered_caller.resume_handle = &test_context_channel_fifo_resume;
590
591 test_context_channel_fifo_context context;
592
593 // Re-adding the same handle while it's the only caller keeps a single caller.
594 context.add_caller(first_caller);
595 context.add_caller(first_caller);
596 CASE_EXPECT_FALSE(context.has_multiple_callers());
597
598 // Removing a handle that was never registered returns false while single caller.
599 CASE_EXPECT_FALSE(context.remove_caller(second_caller));
600
601 // Convert to multiple callers, then a repeated registration is still ignored.
602 context.add_caller(second_caller);
603 context.add_caller(first_caller);
604 CASE_EXPECT_TRUE(context.has_multiple_callers());
605
606 // Removing a handle not present in multi-caller mode returns false.
607 CASE_EXPECT_FALSE(context.remove_caller(unregistered_caller));
608
609 // Removing the registered handles returns true and empties the caller set.
610 CASE_EXPECT_TRUE(context.remove_caller(first_caller));
611 // Re-adding the remaining caller must not duplicate it into the vacant single-caller slot.
612 context.add_caller(second_caller);
613 CASE_EXPECT_FALSE(context.has_multiple_callers());
614 CASE_EXPECT_TRUE(context.remove_caller(second_caller));
615 CASE_EXPECT_FALSE(context.remove_caller(second_caller));
616
617 g_test_coroutine_channel_fifo_order.clear();
618 CASE_EXPECT_EQ(0, static_cast<int>(context.resume_callers()));
619 CASE_EXPECT_TRUE(g_test_coroutine_channel_fifo_order.empty());
620
621 context.add_caller(first_caller);
622 CASE_EXPECT_FALSE(context.has_multiple_callers());
623 CASE_EXPECT_EQ(1, static_cast<int>(context.resume_callers()));
624 CASE_EXPECT_EQ(1, static_cast<int>(g_test_coroutine_channel_fifo_order.size()));
625}
copp::memory::strong_rc_ptr< copp::stackful_channel_sender< T > > sender
copp::stackful_channel_receiver< T > create_channel()
static int g_test_coroutine_channel_status
copp::coroutine_context_container< copp::allocator::stack_allocator_memory > test_context_channel_coroutine_context_test_type
LIBCOPP_COPP_API_HEAD_ONLY Tc * get()
get current coroutine and try to convert type
#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