libcopp 2.3.1
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
coroutine_task_test.cpp
Go to the documentation of this file.
1// Copyright 2023 owent
2
5#include <libcotask/task.h>
6
7#include <cstdio>
8#include <cstring>
9#include <iostream>
10#include <memory>
11#include <set>
12#include <vector>
13
14#include "frame/test_macros.h"
15
16#ifdef LIBCOTASK_MACRO_ENABLED
17static int g_test_coroutine_task_status = 0;
18static int g_test_coroutine_task_on_finished = 0;
19class test_context_task_action_base : public cotask::impl::task_action_impl {
20 public:
21 // add a same name function to find the type detection error
22 virtual int operator()() = 0;
23
24 int operator()(void *priv_data) override {
25 ++g_test_coroutine_task_status;
26
27 CASE_EXPECT_EQ(&g_test_coroutine_task_status, priv_data);
28
29 CASE_EXPECT_EQ(cotask::EN_TS_RUNNING, cotask::this_task::get_task()->get_status());
30 cotask::this_task::get_task()->yield(&priv_data);
31 CASE_EXPECT_EQ(cotask::EN_TS_RUNNING, cotask::this_task::get_task()->get_status());
32
33 CASE_EXPECT_EQ(cotask::this_task::get_task(), priv_data);
34
35 ++g_test_coroutine_task_status;
36
37 return 0;
38 }
39
40 int on_finished(cotask::impl::task_impl &) override {
41 ++g_test_coroutine_task_on_finished;
42 return 0;
43 }
44};
45
46class test_context_task_action : public test_context_task_action_base {
47 public:
48 using test_context_task_action_base::operator();
49
50 // add a same name function to find the type detection error
51 int operator()() override { return 0; }
52};
53
54CASE_TEST(coroutine_task, custom_action) {
55 typedef cotask::task<>::ptr_t task_ptr_type;
56 g_test_coroutine_task_status = 0;
57 g_test_coroutine_task_on_finished = 0;
58
59 {
60 task_ptr_type co_task = cotask::task<>::create(test_context_task_action());
61 task_ptr_type co_another_task = cotask::task<>::create(test_context_task_action()); // share action
62
63 CASE_EXPECT_EQ(cotask::EN_TS_CREATED, co_task->get_status());
64 CASE_EXPECT_EQ(cotask::EN_TS_CREATED, co_another_task->get_status());
65
66 CASE_EXPECT_EQ(0, co_task->start(&g_test_coroutine_task_status));
67
68 CASE_EXPECT_EQ(g_test_coroutine_task_status, 1);
69 CASE_EXPECT_FALSE(co_task->is_completed());
70 CASE_EXPECT_FALSE(co_task->is_canceled());
71 CASE_EXPECT_FALSE(co_task->is_faulted());
72
73 CASE_EXPECT_EQ(0, co_another_task->start(&g_test_coroutine_task_status));
74 CASE_EXPECT_EQ(g_test_coroutine_task_status, 2);
75
76 CASE_EXPECT_EQ(cotask::EN_TS_WAITING, co_task->get_status());
77 CASE_EXPECT_EQ(cotask::EN_TS_WAITING, co_another_task->get_status());
78
79 CASE_EXPECT_EQ(0, co_task->resume(co_task.get()));
80 CASE_EXPECT_EQ(g_test_coroutine_task_status, 3);
81
82 CASE_EXPECT_EQ(0, co_another_task->resume(co_another_task.get()));
83 CASE_EXPECT_EQ(g_test_coroutine_task_status, 4);
84
85 CASE_EXPECT_TRUE(co_task->is_completed());
86 CASE_EXPECT_TRUE(co_another_task->is_completed());
87 CASE_EXPECT_FALSE(co_task->is_canceled());
88 CASE_EXPECT_FALSE(co_task->is_faulted());
89
90 CASE_EXPECT_EQ(co_task->get_status(), cotask::EN_TS_DONE);
91 CASE_EXPECT_EQ(co_another_task->get_status(), cotask::EN_TS_DONE);
92
93 CASE_EXPECT_GT(0, co_another_task->resume(co_another_task.get()));
94 CASE_EXPECT_EQ(g_test_coroutine_task_status, 4);
95
96 ++g_test_coroutine_task_status;
97 CASE_EXPECT_EQ(g_test_coroutine_task_status, 5);
98
99 CASE_EXPECT_NE(co_task->get_id(), 0);
100 }
101
102 CASE_EXPECT_EQ(g_test_coroutine_task_on_finished, 2);
103}
104
105struct test_context_task_functor {
106 public:
107 int operator()(void *) const {
108 ++g_test_coroutine_task_status;
109 CASE_EXPECT_EQ(g_test_coroutine_task_status, 1);
110
111 cotask::this_task::get_task()->yield();
112
113 ++g_test_coroutine_task_status;
114 CASE_EXPECT_EQ(g_test_coroutine_task_status, 3);
115
116 return 0;
117 }
118};
119
120CASE_TEST(coroutine_task, functor_action) {
121 typedef cotask::task<>::ptr_t task_ptr_type;
122 task_ptr_type co_task = cotask::task<>::create(test_context_task_functor());
123 g_test_coroutine_task_status = 0;
124
125 CASE_EXPECT_EQ(0, co_task->start());
126
127 ++g_test_coroutine_task_status;
128 CASE_EXPECT_EQ(g_test_coroutine_task_status, 2);
129
130 CASE_EXPECT_FALSE(co_task->is_completed());
131 CASE_EXPECT_EQ(0, co_task->resume());
132
133 CASE_EXPECT_TRUE(co_task->is_completed());
134
135 ++g_test_coroutine_task_status;
136 CASE_EXPECT_EQ(g_test_coroutine_task_status, 4);
137
138 CASE_EXPECT_NE(co_task->get_id(), 0);
139}
140
141static int test_context_task_function_1(void *) {
142 ++g_test_coroutine_task_status;
143 CASE_EXPECT_EQ(g_test_coroutine_task_status, 1);
144
145 cotask::this_task::get_task()->yield();
146
147 ++g_test_coroutine_task_status;
148 CASE_EXPECT_EQ(g_test_coroutine_task_status, 3);
149
150 return 100;
151}
152
153static void test_context_task_function_2(void *) {
154 ++g_test_coroutine_task_status;
155 CASE_EXPECT_EQ(g_test_coroutine_task_status, 1);
156
157 cotask::this_task::get_task()->yield();
158
159 ++g_test_coroutine_task_status;
160 CASE_EXPECT_EQ(g_test_coroutine_task_status, 3);
161}
162
163CASE_TEST(coroutine_task, function_action) {
164 {
165 typedef cotask::task<>::ptr_t task_ptr_type;
166 task_ptr_type co_task = cotask::task<>::create(test_context_task_function_1);
167 g_test_coroutine_task_status = 0;
168
169 CASE_EXPECT_EQ(0, co_task->start());
170
171 ++g_test_coroutine_task_status;
172 CASE_EXPECT_EQ(g_test_coroutine_task_status, 2);
173
174 CASE_EXPECT_FALSE(co_task->is_completed());
175 CASE_EXPECT_EQ(0, co_task->resume());
176
177 CASE_EXPECT_TRUE(co_task->is_completed());
178
179 ++g_test_coroutine_task_status;
180 CASE_EXPECT_EQ(g_test_coroutine_task_status, 4);
181 CASE_EXPECT_EQ(co_task->get_coroutine_context()->get_ret_code(), 100);
182 }
183
184 {
185 typedef cotask::task<>::ptr_t task_ptr_type;
186 task_ptr_type co_task = cotask::task<>::create(test_context_task_function_2);
187 g_test_coroutine_task_status = 0;
188
189 CASE_EXPECT_EQ(0, co_task->start());
190
191 ++g_test_coroutine_task_status;
192 CASE_EXPECT_EQ(g_test_coroutine_task_status, 2);
193
194 CASE_EXPECT_FALSE(co_task->is_completed());
195 CASE_EXPECT_EQ(0, co_task->resume());
196
197 CASE_EXPECT_TRUE(co_task->is_completed());
198
199 ++g_test_coroutine_task_status;
200 CASE_EXPECT_EQ(g_test_coroutine_task_status, 4);
201 }
202}
203
204CASE_TEST(coroutine_task, cancel) {
205 typedef cotask::task<>::ptr_t task_ptr_type;
206 task_ptr_type co_task = cotask::task<>::create(test_context_task_function_1);
207 g_test_coroutine_task_status = 0;
208
209 CASE_EXPECT_EQ(0, co_task->start());
210
211 ++g_test_coroutine_task_status;
212 CASE_EXPECT_EQ(g_test_coroutine_task_status, 2);
213
214 CASE_EXPECT_FALSE(co_task->is_completed());
215 CASE_EXPECT_EQ(0, co_task->cancel(nullptr));
216
217 CASE_EXPECT_TRUE(co_task->is_completed());
218}
219
220// task start and coroutine context yield
221static void test_context_task_function_3(void *) {
222 ++g_test_coroutine_task_status;
223 CASE_EXPECT_EQ(g_test_coroutine_task_status, 1);
224
225 CASE_EXPECT_EQ(cotask::EN_TS_RUNNING, cotask::this_task::get_task()->get_status());
226
227 copp::this_coroutine::yield();
228
229 CASE_EXPECT_EQ(cotask::EN_TS_RUNNING, cotask::this_task::get<cotask::task<> >()->get_status());
230
231 ++g_test_coroutine_task_status;
232 CASE_EXPECT_EQ(g_test_coroutine_task_status, 3);
233}
234
235CASE_TEST(coroutine_task, coroutine_context_yield) {
236 typedef cotask::task<>::ptr_t task_ptr_type;
237 task_ptr_type co_task = cotask::task<>::create(test_context_task_function_3);
238 g_test_coroutine_task_status = 0;
239
240 CASE_EXPECT_EQ(0, co_task->start());
241
242 ++g_test_coroutine_task_status;
243 CASE_EXPECT_EQ(g_test_coroutine_task_status, 2);
244
245 CASE_EXPECT_FALSE(co_task->is_completed());
246 CASE_EXPECT_EQ(cotask::EN_TS_WAITING, co_task->get_status());
247 CASE_EXPECT_EQ(0, co_task->resume());
248
249 CASE_EXPECT_TRUE(co_task->is_completed());
250
251 ++g_test_coroutine_task_status;
252 CASE_EXPECT_EQ(g_test_coroutine_task_status, 4);
253
254 CASE_EXPECT_NE(co_task->get_id(), 0);
255}
256
257struct test_context_task_mem_function {
258 cotask::task<>::id_t task_id_;
259
260 int real_run(void *) {
261 ++g_test_coroutine_task_status;
262 CASE_EXPECT_EQ(g_test_coroutine_task_status, 1);
263
264 CASE_EXPECT_EQ(task_id_, cotask::task<>::this_task()->get_id());
265 cotask::task<>::this_task()->yield();
266 CASE_EXPECT_EQ(task_id_, cotask::task<>::this_task()->get_id());
267
268 ++g_test_coroutine_task_status;
269 CASE_EXPECT_EQ(g_test_coroutine_task_status, 3);
270
271 return -1;
272 }
273};
274
275CASE_TEST(coroutine_task, mem_function_action) {
276 typedef cotask::task<>::ptr_t task_ptr_type;
277 test_context_task_mem_function obj;
278 task_ptr_type co_task = cotask::task<>::create(&test_context_task_mem_function::real_run, &obj);
279 g_test_coroutine_task_status = 0;
280 obj.task_id_ = co_task->get_id();
281
282 CASE_EXPECT_EQ(0, co_task->start());
283
284 ++g_test_coroutine_task_status;
285 CASE_EXPECT_EQ(g_test_coroutine_task_status, 2);
286
287 CASE_EXPECT_FALSE(co_task->is_completed());
288 CASE_EXPECT_EQ(0, co_task->resume());
289
290 CASE_EXPECT_TRUE(co_task->is_completed());
291
292 ++g_test_coroutine_task_status;
293 CASE_EXPECT_EQ(g_test_coroutine_task_status, 4);
294
295 CASE_EXPECT_NE(co_task->get_coroutine_context()->get_ret_code(), -1);
296}
297
298CASE_TEST(coroutine_task, auto_finish) {
299 typedef cotask::task<>::ptr_t task_ptr_type;
300 {
301 test_context_task_mem_function obj;
302 task_ptr_type co_task = cotask::task<>::create(&test_context_task_mem_function::real_run, &obj);
303 g_test_coroutine_task_status = 0;
304 obj.task_id_ = co_task->get_id();
305 }
306 CASE_EXPECT_EQ(0, g_test_coroutine_task_status);
307
308 {
309 test_context_task_mem_function obj;
310 task_ptr_type co_task = cotask::task<>::create(&test_context_task_mem_function::real_run, &obj);
311 g_test_coroutine_task_status = 0;
312 obj.task_id_ = co_task->get_id();
313
314 CASE_EXPECT_EQ(0, co_task->start());
315
316 ++g_test_coroutine_task_status;
317 CASE_EXPECT_EQ(g_test_coroutine_task_status, 2);
318
319 CASE_EXPECT_FALSE(co_task->is_completed());
320 }
321
322 ++g_test_coroutine_task_status;
323 CASE_EXPECT_EQ(g_test_coroutine_task_status, 4);
324}
325
326struct test_context_task_next_action : public cotask::impl::task_action_impl {
327 int set_;
328 int check_;
329 test_context_task_next_action(int s, int c) : cotask::impl::task_action_impl(), set_(s), check_(c) {}
330
331 int operator()(void *) override {
332 CASE_EXPECT_EQ(g_test_coroutine_task_status, check_);
333 g_test_coroutine_task_status = set_;
334
335 CASE_EXPECT_EQ(copp::COPP_EC_IS_RUNNING, cotask::this_task::get_task()->start());
336
337 ++g_test_coroutine_task_on_finished;
338 return 0;
339 }
340};
341
342CASE_TEST(coroutine_task, next) {
343 typedef cotask::task<>::ptr_t task_ptr_type;
344
345 {
346 g_test_coroutine_task_status = 0;
347
348 task_ptr_type co_task = cotask::task<>::create(test_context_task_next_action(15, 0));
349 co_task->next(test_context_task_next_action(7, 15))
350 ->next(test_context_task_next_action(99, 7))
351 ->next(test_context_task_next_action(1023, 99))
352 ->next(test_context_task_next_action(5, 1023));
353
354 CASE_EXPECT_EQ(co_task->next(co_task), co_task);
355 CASE_EXPECT_EQ(0, co_task->start());
356 CASE_EXPECT_EQ(g_test_coroutine_task_status, 5);
357
358 CASE_EXPECT_EQ(copp::COPP_EC_ALREADY_FINISHED, co_task->start());
359 }
360
361 {
362 g_test_coroutine_task_status = 0;
363 task_ptr_type co_task_a = cotask::task<>::create(test_context_task_next_action(2, 1));
364 task_ptr_type co_task_b = cotask::task<>::create(test_context_task_function_1);
365
366 CASE_EXPECT_EQ(0, co_task_b->start());
367 co_task_a->next(co_task_b);
368 CASE_EXPECT_EQ(0, co_task_a->start());
369 CASE_EXPECT_TRUE(co_task_a->is_completed());
370 CASE_EXPECT_TRUE(co_task_b->is_completed());
371 CASE_EXPECT_TRUE(co_task_b->::cotask::impl::task_impl::is_completed());
372 }
373}
374
375struct test_context_task_functor_drived : public cotask::impl::task_action_impl {
376 public:
377 int a_;
378 int b_;
379 test_context_task_functor_drived(int a, int b) : a_(a), b_(b) {}
380
381 int operator()(void *) override {
382 CASE_EXPECT_EQ(a_, 1);
383 CASE_EXPECT_EQ(3, b_);
384
385 return 0;
386 }
387};
388
389CASE_TEST(coroutine_task, functor_drived_action) {
390 typedef cotask::task<>::ptr_t task_ptr_type;
391 cotask::task<>::coroutine_type::allocator_type alloc;
392 task_ptr_type co_task = cotask::task<>::create_with<test_context_task_functor_drived>(alloc, 0, 0, 1, 3);
393 CASE_EXPECT_EQ(0, co_task->start());
394}
395
396static int test_context_task_priavte_buffer(void *) {
397 void *priv_data = cotask::task<>::this_task()->get_private_buffer();
398 size_t priv_sz = cotask::task<>::this_task()->get_private_buffer_size();
399
400 CASE_EXPECT_GE(priv_sz, 256);
401
402 if (priv_sz >= 256) {
403 char checked_data[256];
404 memset(checked_data, 0x5e, 256);
405
406 CASE_EXPECT_EQ(0, memcmp(priv_data, checked_data, 256));
407 }
408
409 return 0;
410}
411
412CASE_TEST(coroutine_task, priavte_buffer) {
413 typedef cotask::task<>::ptr_t task_ptr_type;
414 task_ptr_type co_task = cotask::task<>::create(test_context_task_priavte_buffer, 16384, 256);
415
416 void *priv_data = co_task->get_private_buffer();
417 memset(priv_data, 0x5e, 256);
418
419 CASE_EXPECT_EQ(0, co_task->start());
420}
421
422static int test_context_task_timeout(void *) {
423 cotask::task<>::this_task()->yield();
424
425 CASE_EXPECT_TRUE(cotask::task<>::this_task()->is_timeout());
426 CASE_EXPECT_TRUE(cotask::task<>::this_task()->is_faulted());
427 CASE_EXPECT_FALSE(cotask::task<>::this_task()->is_completed());
428 CASE_EXPECT_TRUE(cotask::task<>::this_task()->is_exiting());
429
430 return 0;
431}
432
433CASE_TEST(coroutine_task, kill_and_timeout) {
434 typedef cotask::task<>::ptr_t task_ptr_type;
435 task_ptr_type co_task = cotask::task<>::create(test_context_task_timeout, 16384, 256);
436
437 void *priv_data = co_task->get_private_buffer();
438 memset(priv_data, 0x5e, 256);
439
440 CASE_EXPECT_EQ(0, co_task->start());
441
442 CASE_EXPECT_FALSE(co_task->is_timeout());
443 CASE_EXPECT_FALSE(co_task->is_faulted());
444 CASE_EXPECT_FALSE(co_task->is_completed());
445 CASE_EXPECT_FALSE(co_task->is_exiting());
446
447 CASE_EXPECT_EQ(0, co_task->kill(cotask::EN_TS_TIMEOUT, nullptr));
448
449 CASE_EXPECT_TRUE(co_task->is_completed());
450
451 CASE_EXPECT_NE(nullptr, co_task->get_raw_action());
452}
453
454static int test_context_task_await_1(void *) {
455 typedef cotask::task<>::ptr_t task_ptr_type;
456
457 task_ptr_type self = cotask::task<>::this_task();
458
459 CASE_EXPECT_EQ(copp::COPP_EC_ARGS_ERROR, self->await_task(nullptr));
460 CASE_EXPECT_EQ(copp::COPP_EC_TASK_CAN_NOT_WAIT_SELF, self->await_task(self));
461
462 void *priv_data = self->get_private_buffer();
463 cotask::task<>::self_t *other_task = *reinterpret_cast<cotask::task<>::self_t **>(priv_data);
464
465 if (other_task->is_exiting()) {
466 CASE_EXPECT_EQ(copp::COPP_EC_TASK_IS_EXITING, self->await_task(other_task));
467 } else {
468 if (self->is_exiting()) {
469 CASE_EXPECT_EQ(copp::COPP_EC_TASK_IS_EXITING, self->await_task(other_task));
470 } else {
471 CASE_EXPECT_EQ(0, self->await_task(other_task));
472 }
473 }
474
475 return 0;
476}
477
478static int test_context_task_await_2(void *) { return 0; }
479
480CASE_TEST(coroutine_task, await_task) {
481 typedef cotask::task<>::ptr_t task_ptr_type;
482
483 // normal
484 {
485 task_ptr_type co_task_1 =
486 cotask::task<>::create(test_context_task_await_1, 16384, sizeof(cotask::task<>::self_t *));
487 task_ptr_type co_task_2 = cotask::task<>::create(test_context_task_await_2, 16384);
488
489 void *priv_data = co_task_1->get_private_buffer();
490
491 *reinterpret_cast<cotask::task<>::self_t **>(priv_data) = co_task_2.get();
492 CASE_EXPECT_EQ(copp::COPP_EC_TASK_NOT_IN_ACTION, co_task_1->await_task(co_task_2));
493
494 CASE_EXPECT_FALSE(co_task_1->is_exiting());
495 CASE_EXPECT_FALSE(co_task_2->is_exiting());
496
497 co_task_1->start();
498 CASE_EXPECT_FALSE(co_task_1->is_exiting());
499 CASE_EXPECT_FALSE(co_task_2->is_exiting());
500
501 co_task_2->start();
502 CASE_EXPECT_TRUE(co_task_1->is_exiting());
503 CASE_EXPECT_TRUE(co_task_2->is_exiting());
504 }
505
506 // co_task_1 exiting
507 {
508 task_ptr_type co_task_1 =
509 cotask::task<>::create(test_context_task_await_1, 16384, sizeof(cotask::task<>::self_t *));
510 task_ptr_type co_task_2 = cotask::task<>::create(test_context_task_await_2, 16384);
511
512 void *priv_data = co_task_1->get_private_buffer();
513
514 CASE_EXPECT_EQ(copp::COPP_EC_ARGS_ERROR, co_task_1->await_task(nullptr));
515 CASE_EXPECT_EQ(copp::COPP_EC_TASK_CAN_NOT_WAIT_SELF, co_task_1->await_task(co_task_1));
516
517 *reinterpret_cast<cotask::task<>::self_t **>(priv_data) = co_task_2.get();
518 CASE_EXPECT_EQ(copp::COPP_EC_TASK_NOT_IN_ACTION, co_task_1->await_task(co_task_2));
519
520 CASE_EXPECT_FALSE(co_task_1->is_exiting());
521 CASE_EXPECT_FALSE(co_task_2->is_exiting());
522
523 co_task_1->kill(cotask::EN_TS_TIMEOUT);
524 CASE_EXPECT_TRUE(co_task_1->is_exiting());
525 co_task_1->start();
526 CASE_EXPECT_TRUE(co_task_1->is_exiting());
527 CASE_EXPECT_FALSE(co_task_2->is_exiting());
528
529 co_task_2->start();
530 CASE_EXPECT_TRUE(co_task_1->is_exiting());
531 CASE_EXPECT_TRUE(co_task_2->is_exiting());
532 }
533
534 // co_task_2 exiting
535 {
536 task_ptr_type co_task_1 =
537 cotask::task<>::create(test_context_task_await_1, 16384, sizeof(cotask::task<>::self_t *));
538 task_ptr_type co_task_2 = cotask::task<>::create(test_context_task_await_2, 16384);
539
540 void *priv_data = co_task_1->get_private_buffer();
541
542 *reinterpret_cast<cotask::task<>::self_t **>(priv_data) = co_task_2.get();
543 CASE_EXPECT_EQ(copp::COPP_EC_TASK_NOT_IN_ACTION, co_task_1->await_task(co_task_2));
544
545 CASE_EXPECT_FALSE(co_task_1->is_exiting());
546 CASE_EXPECT_FALSE(co_task_2->is_exiting());
547
548 co_task_2->start();
549 CASE_EXPECT_FALSE(co_task_1->is_exiting());
550 CASE_EXPECT_TRUE(co_task_2->is_exiting());
551
552 co_task_1->start();
553 CASE_EXPECT_TRUE(co_task_1->is_exiting());
554 CASE_EXPECT_TRUE(co_task_2->is_exiting());
555 }
556}
557
558static int test_context_task_then_action_func(void *priv_data) {
559 CASE_EXPECT_EQ(&g_test_coroutine_task_status, priv_data);
560 ++g_test_coroutine_task_on_finished;
561 return 0;
562}
563
564CASE_TEST(coroutine_task, then) {
565 typedef cotask::task<>::ptr_t task_ptr_type;
566 g_test_coroutine_task_status = 0;
567 g_test_coroutine_task_on_finished = 0;
568
569 task_ptr_type co_task = cotask::task<>::create(test_context_task_next_action(15, 0));
570 co_task->then(test_context_task_next_action(7, 15))
571 ->then(test_context_task_next_action(99, 7))
572 ->then(test_context_task_next_action(1023, 99))
573 ->then(test_context_task_next_action(5, 1023));
574
575 CASE_EXPECT_EQ(0, co_task->start());
576 CASE_EXPECT_EQ(g_test_coroutine_task_status, 5);
577
578 CASE_EXPECT_EQ(copp::COPP_EC_ALREADY_FINISHED, co_task->start());
579
580 CASE_EXPECT_TRUE(co_task->is_exiting());
581 CASE_EXPECT_TRUE(co_task->is_completed());
582
583 co_task->then(test_context_task_next_action(127, 5))
584 ->then(test_context_task_then_action_func, &g_test_coroutine_task_status);
585 CASE_EXPECT_EQ(g_test_coroutine_task_status, 127);
586 CASE_EXPECT_EQ(g_test_coroutine_task_on_finished, 7);
587}
588
589typedef copp::stack_pool<copp::allocator::stack_allocator_malloc> test_context_task_stack_pool_t;
590struct test_context_task_stack_pool_test_macro_coroutine {
591 using stack_allocator_type = copp::allocator::stack_allocator_pool<test_context_task_stack_pool_t>;
592 using coroutine_type = copp::coroutine_context_container<stack_allocator_type>;
593 using value_type = int;
594};
595
596typedef cotask::task<test_context_task_stack_pool_test_macro_coroutine> test_context_task_stack_pool_test_task_t;
597
598CASE_TEST(coroutine_task, then_with_stack_pool) {
599 typedef test_context_task_stack_pool_test_task_t::ptr_t task_ptr_type;
600 test_context_task_stack_pool_t::ptr_t stack_pool = test_context_task_stack_pool_t::create();
601
602 g_test_coroutine_task_on_finished = 0;
603 g_test_coroutine_task_status = 0;
604
605 copp::allocator::stack_allocator_pool<test_context_task_stack_pool_t> base_alloc(stack_pool);
606 task_ptr_type tp = test_context_task_stack_pool_test_task_t::create(test_context_task_next_action(15, 0), base_alloc);
607 tp->then(test_context_task_next_action(127, 15))
608 ->then(test_context_task_then_action_func, &g_test_coroutine_task_status);
609
611 tp->start();
613
614 tp->then(test_context_task_next_action(255, 127))
615 ->then(test_context_task_then_action_func, &g_test_coroutine_task_status);
616
618 CASE_EXPECT_EQ(g_test_coroutine_task_status, 255);
619 CASE_EXPECT_EQ(g_test_coroutine_task_on_finished, 5);
620}
621
622// Issue #18: https://github.com/owent/libcopp/issues/18
623
624class task_action_of_issue18 {
625 public:
626 task_action_of_issue18() {}
627 int operator()(void *) { return 0; }
628};
629
630CASE_TEST(coroutine_task, github_issues_18) {
631 using simple_task_t = cotask::task<>;
632
633 {
634 simple_task_t::ptr_t co_task = simple_task_t::create(task_action_of_issue18());
635 if (co_task) {
636 co_task->then(task_action_of_issue18());
637 }
638 }
639
640 {
641 task_action_of_issue18 copy_action;
642 simple_task_t::ptr_t co_task = simple_task_t::create(copy_action);
643 if (co_task) {
644 co_task->then(copy_action);
645 }
646 }
647}
648
649# if LIBCOPP_MACRO_ENABLE_MULTI_THREAD
650static LIBCOPP_COPP_NAMESPACE_ID::util::lock::atomic_int_type<int> g_test_context_task_test_atomic;
651static constexpr const int g_test_context_task_test_mt_run_times = 10000;
652static size_t g_test_context_task_test_mt_max_run_thread_number = 0;
653enum {
654 test_context_task_test_mt_thread_num = 100,
655 test_context_task_test_mt_task_num = 1000,
656};
657
658struct test_context_task_test_action_mt_thread : public cotask::impl::task_action_impl {
659 int run_count;
660
661 public:
662 test_context_task_test_action_mt_thread() : run_count(0) {}
663
664 int operator()(void *thread_func_address) override {
665 std::set<void *> thread_counter;
666
667 while (run_count < g_test_context_task_test_mt_run_times) {
668 ++run_count;
669 ++g_test_context_task_test_atomic;
670
671 thread_counter.insert(thread_func_address);
672
673 cotask::this_task::get_task()->yield(&thread_func_address);
674 }
675
676 if (g_test_context_task_test_mt_max_run_thread_number < thread_counter.size()) {
677 g_test_context_task_test_mt_max_run_thread_number = thread_counter.size();
678 }
679 return 0;
680 }
681};
682
683struct test_context_task_test_mt_thread_runner {
684 typedef cotask::task<>::ptr_t task_ptr_type;
685
686 std::vector<task_ptr_type> *task_pool;
687 test_context_task_test_mt_thread_runner(std::vector<task_ptr_type> *pool) : task_pool(pool) {}
688
689 int operator()() {
690 bool need_continue = true;
691 while (need_continue) {
692 need_continue = false;
693
694 for (size_t i = 0; i < task_pool->size(); ++i) {
695 if (false == (*task_pool)[i]->is_completed()) {
696 need_continue = true;
697 (*task_pool)[i]->resume(this);
698 }
699 }
700 }
701 return 0;
702 }
703};
704
705CASE_TEST(coroutine_task, mt_run_competition) {
706 typedef cotask::task<>::ptr_t task_ptr_type;
707 std::vector<task_ptr_type> task_pool;
708 task_pool.reserve(test_context_task_test_mt_task_num);
709 for (int i = 0; i < test_context_task_test_mt_task_num; ++i) {
710 task_pool.push_back(
711 cotask::task<>::create(test_context_task_test_action_mt_thread(), 16 * 1024)); // use 16KB for stack
712 }
713
714 g_test_context_task_test_atomic.store(0);
715 g_test_context_task_test_mt_max_run_thread_number = 0;
716
717 std::unique_ptr<std::thread> thds[test_context_task_test_mt_thread_num];
718 for (int i = 0; i < test_context_task_test_mt_thread_num; ++i) {
719 thds[i].reset(new std::thread(test_context_task_test_mt_thread_runner(&task_pool)));
720 }
721
722 for (int i = 0; i < test_context_task_test_mt_thread_num; ++i) {
723 thds[i]->join();
724 }
725
726 CASE_EXPECT_EQ(g_test_context_task_test_mt_run_times * test_context_task_test_mt_task_num,
727 g_test_context_task_test_atomic.load());
728 CASE_MSG_INFO() << "Coroutine tasks are run on " << g_test_context_task_test_mt_max_run_thread_number
729 << " threads at most." << std::endl;
730}
731# endif
732
733# if defined(LIBCOPP_MACRO_ENABLE_STD_COROUTINE) && LIBCOPP_MACRO_ENABLE_STD_COROUTINE
734static copp::callable_future<int> call_for_await_cotask(const cotask::task<>::ptr_t &t) {
735 if (t) {
736 co_return co_await t;
737 }
738
739 co_return 0;
740}
741
742static int cotask_action_callback(void *) {
743 int ret = 234;
744 void *ptr = nullptr;
745 cotask::this_task::get_task()->yield(&ptr);
746 if (ptr != nullptr) {
747 ret = *reinterpret_cast<int *>(ptr);
748 }
749 return ret;
750}
751
752CASE_TEST(coroutine_task, callable_future_co_await) {
753 cotask::task<>::ptr_t co_task = cotask::task<>::create(cotask_action_callback);
754
755 auto t = call_for_await_cotask(co_task);
756 co_task->start();
757 CASE_EXPECT_FALSE(t.is_ready());
758
759 int res = 345;
760 co_task->resume(reinterpret_cast<void *>(&res));
761
762 CASE_EXPECT_TRUE(t.is_ready());
763
764 CASE_EXPECT_EQ(res, t.get_internal_promise().data());
765}
766
767CASE_TEST(coroutine_task, callable_future_co_await_do_not_start) {
768 cotask::task<>::ptr_t co_task = cotask::task<>::create(cotask_action_callback);
769
770 auto t = call_for_await_cotask(co_task);
771 CASE_EXPECT_FALSE(t.is_ready());
772
773 co_task.reset();
774
775 CASE_EXPECT_TRUE(t.is_ready());
776
777 CASE_EXPECT_EQ(LIBCOPP_COPP_NAMESPACE_ID::COPP_EC_TASK_IS_KILLED, t.get_internal_promise().data());
778}
779
780# endif
781
782#endif
const limit_t & get_limit() const
Definition stack_pool.h:73
std::shared_ptr< cli::cmd_option_value > value_type
Definition cmd_option.h:50
my_task_t::ptr_t task_ptr_type
#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_EXPECT_GT(l, r)
#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
#define CASE_EXPECT_GE(l, r)