libcopp 2.3.1
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
coroutine_task_manager_test.cpp
Go to the documentation of this file.
1// Copyright 2023 owent
2
3#include <libcotask/task.h>
5
6#include <cstdio>
7#include <cstring>
8#include <iostream>
9#include <memory>
10
11#include "frame/test_macros.h"
12
13#ifdef LIBCOTASK_MACRO_ENABLED
14
15static int g_test_coroutine_task_manager_status = 0;
16class test_context_task_manager_action : public cotask::impl::task_action_impl {
17 public:
18 int operator()(void *) override {
19 ++g_test_coroutine_task_manager_status;
20
21 // CASE_EXPECT_EQ(cotask::EN_TS_RUNNING, cotask::this_task::get_task()->get_status());
22 // may be EN_TS_RUNNING or EN_TS_CANCELED or EN_TS_KILLED or EN_TS_TIMEOUT
23 cotask::this_task::get_task()->yield();
24
25 ++g_test_coroutine_task_manager_status;
26
27 return 0;
28 }
29};
30
31CASE_TEST(coroutine_task_manager, tickspec_t) {
32 cotask::detail::tickspec_t l;
33 cotask::detail::tickspec_t r;
34
35 l.tv_sec = 123;
36 l.tv_nsec = 456;
37
38 r.tv_sec = 123;
39 r.tv_nsec = 456;
40
41 CASE_EXPECT_TRUE(l == r);
42 CASE_EXPECT_FALSE(l != r);
43 CASE_EXPECT_FALSE(l < r);
44 CASE_EXPECT_TRUE(l <= r);
45
46 r.tv_sec = 456;
47 r.tv_nsec = 123;
48
49 CASE_EXPECT_FALSE(l == r);
50 CASE_EXPECT_TRUE(l != r);
51 CASE_EXPECT_TRUE(l < r);
52 CASE_EXPECT_TRUE(l <= r);
53
54 r.tv_sec = 45;
55 r.tv_nsec = 999;
56
57 CASE_EXPECT_FALSE(l == r);
58 CASE_EXPECT_TRUE(l != r);
59 CASE_EXPECT_FALSE(l < r);
60 CASE_EXPECT_FALSE(l <= r);
61}
62
63CASE_TEST(coroutine_task_manager, task_timer_node) {
64 cotask::detail::task_timer_node<cotask::task<>::id_type> l;
65 cotask::detail::task_timer_node<cotask::task<>::id_type> r;
66
67 l.expired_time.tv_sec = 123;
68 l.expired_time.tv_nsec = 456;
69 l.task_id = 10;
70
71 r.expired_time.tv_sec = 123;
72 r.expired_time.tv_nsec = 456;
73 r.task_id = 10;
74
75 CASE_EXPECT_TRUE(l == r);
76 CASE_EXPECT_FALSE(l != r);
77 CASE_EXPECT_FALSE(l < r);
78 CASE_EXPECT_TRUE(l <= r);
79
80 r.task_id = 5;
81
82 CASE_EXPECT_FALSE(l == r);
83 CASE_EXPECT_TRUE(l != r);
84 CASE_EXPECT_FALSE(l < r);
85 CASE_EXPECT_FALSE(l <= r);
86
87 r.expired_time.tv_nsec = 45;
88 r.task_id = 10;
89
90 CASE_EXPECT_FALSE(l == r);
91 CASE_EXPECT_TRUE(l != r);
92 CASE_EXPECT_FALSE(l < r);
93 CASE_EXPECT_FALSE(l <= r);
94
95 r.expired_time.tv_nsec = 456;
96 r.task_id = 15;
97
98 CASE_EXPECT_FALSE(l == r);
99 CASE_EXPECT_TRUE(l != r);
100 CASE_EXPECT_TRUE(l < r);
101 CASE_EXPECT_TRUE(l <= r);
102
103 r.expired_time.tv_nsec = 4567;
104 r.task_id = 10;
105
106 CASE_EXPECT_FALSE(l == r);
107 CASE_EXPECT_TRUE(l != r);
108 CASE_EXPECT_TRUE(l < r);
109 CASE_EXPECT_TRUE(l <= r);
110}
111
112CASE_TEST(coroutine_task_manager, add_and_timeout) {
113 typedef cotask::task<>::ptr_t task_ptr_type;
114 task_ptr_type co_task = cotask::task<>::create(test_context_task_manager_action());
115 task_ptr_type co_another_task = cotask::task<>::create(test_context_task_manager_action()); // share action
116
117 typedef cotask::task_manager<cotask::task<> > mgr_t;
118 mgr_t::ptr_t task_mgr = mgr_t::create();
119
120 CASE_EXPECT_EQ(0, (int)task_mgr->get_task_size());
121 g_test_coroutine_task_manager_status = 0;
122
123 task_mgr->add_task(co_task, 5, 0);
124 task_mgr->add_task(co_another_task);
125
126 CASE_EXPECT_EQ(2, (int)task_mgr->get_task_size());
127 CASE_EXPECT_EQ(1, (int)task_mgr->get_tick_checkpoint_size());
128
129 CASE_EXPECT_EQ(co_task, task_mgr->find_task(co_task->get_id()));
130 CASE_EXPECT_EQ(co_another_task, task_mgr->find_task(co_another_task->get_id()));
131
132 CASE_EXPECT_EQ(0, (int)task_mgr->get_last_tick_time().tv_sec);
133 task_mgr->tick(3);
134 CASE_EXPECT_EQ(2, (int)task_mgr->get_task_size());
135 CASE_EXPECT_EQ(1, (int)task_mgr->get_tick_checkpoint_size());
136 task_mgr->tick(8);
137 CASE_EXPECT_EQ(8, (int)task_mgr->get_last_tick_time().tv_sec);
138 // tick reset timeout: 3 + 5 = 8
139 CASE_EXPECT_EQ(8, (int)task_mgr->get_container().find(co_task->get_id())->second.timer_node->expired_time.tv_sec);
140 CASE_EXPECT_EQ(1, (int)task_mgr->get_tick_checkpoint_size());
141 CASE_EXPECT_EQ(1, (int)task_mgr->get_checkpoints().size());
142 CASE_EXPECT_FALSE(cotask::EN_TS_TIMEOUT == co_task->get_status());
143
144 task_mgr->tick(9);
145 CASE_EXPECT_EQ(9, (int)task_mgr->get_last_tick_time().tv_sec);
146 CASE_EXPECT_EQ(1, (int)task_mgr->get_task_size());
147 CASE_EXPECT_EQ(1, (int)task_mgr->get_container().size());
148 CASE_EXPECT_EQ(0, (int)task_mgr->get_tick_checkpoint_size());
149 CASE_EXPECT_TRUE(cotask::EN_TS_TIMEOUT == co_task->get_status());
150
151 CASE_EXPECT_NE(co_task, task_mgr->find_task(co_task->get_id()));
152 CASE_EXPECT_EQ(co_another_task, task_mgr->find_task(co_another_task->get_id()));
153
154 CASE_EXPECT_EQ(2, g_test_coroutine_task_manager_status);
155
156 task_mgr->start(co_another_task->get_id());
157
158 CASE_EXPECT_EQ(cotask::EN_TS_WAITING, co_another_task->get_status());
159
160 CASE_EXPECT_EQ(3, g_test_coroutine_task_manager_status);
161 task_mgr->resume(co_another_task->get_id());
162 CASE_EXPECT_EQ(4, g_test_coroutine_task_manager_status);
163
164 CASE_EXPECT_EQ(cotask::EN_TS_TIMEOUT, co_task->get_status());
165 CASE_EXPECT_EQ(cotask::EN_TS_DONE, co_another_task->get_status());
166
167 CASE_EXPECT_EQ(0, (int)task_mgr->get_task_size());
168 CASE_EXPECT_EQ(0, (int)task_mgr->get_tick_checkpoint_size());
169
170 CASE_EXPECT_EQ(copp::COPP_EC_TASK_IS_EXITING, task_mgr->add_task(co_another_task));
171
172 task_mgr.reset();
173}
174
175CASE_TEST(coroutine_task_manager, kill) {
176 typedef cotask::task<>::ptr_t task_ptr_type;
177 task_ptr_type co_task = cotask::task<>::create(test_context_task_manager_action());
178 task_ptr_type co_another_task = cotask::task<>::create(test_context_task_manager_action()); // share action
179
180 typedef cotask::task_manager<cotask::task<> > mgr_t;
181 mgr_t::ptr_t task_mgr = mgr_t::create();
182
183 CASE_EXPECT_EQ(0, (int)task_mgr->get_task_size());
184 g_test_coroutine_task_manager_status = 0;
185 task_mgr->tick(10, 0);
186
187 task_mgr->add_task(co_task, 10, 0);
188 task_mgr->add_task(co_another_task, 10, 0);
189
190 task_mgr->start(co_task->get_id());
191
192 CASE_EXPECT_EQ(2, (int)task_mgr->get_task_size());
193 CASE_EXPECT_EQ(2, (int)task_mgr->get_tick_checkpoint_size());
194
195 task_mgr->kill(co_task->get_id());
196 task_mgr->cancel(co_another_task->get_id());
197
198 CASE_EXPECT_EQ(4, g_test_coroutine_task_manager_status);
199
200 CASE_EXPECT_EQ(0, (int)task_mgr->get_task_size());
201 CASE_EXPECT_EQ(0, (int)task_mgr->get_tick_checkpoint_size());
202
203 CASE_EXPECT_EQ(cotask::EN_TS_KILLED, co_task->get_status());
204 CASE_EXPECT_EQ(cotask::EN_TS_CANCELED, co_another_task->get_status());
205}
206
207CASE_TEST(coroutine_task_manager, duplicated_checkpoints) {
208 typedef cotask::task<>::ptr_t task_ptr_type;
209 task_ptr_type co_task = cotask::task<>::create(test_context_task_manager_action());
210
211 typedef cotask::task_manager<cotask::task<> > mgr_t;
212 mgr_t::ptr_t task_mgr = mgr_t::create();
213
214 CASE_EXPECT_EQ(0, (int)task_mgr->get_task_size());
215 g_test_coroutine_task_manager_status = 0;
216
217 task_mgr->add_task(co_task, 10, 0);
218 CASE_EXPECT_EQ(1, (int)task_mgr->get_task_size());
219 CASE_EXPECT_EQ(1, (int)task_mgr->get_tick_checkpoint_size());
220
221 task_mgr->tick(5);
222
223 CASE_EXPECT_EQ(1, (int)task_mgr->get_task_size());
224 CASE_EXPECT_EQ(1, (int)task_mgr->get_tick_checkpoint_size());
225
226 task_mgr->tick(15, 1);
227
228 CASE_EXPECT_EQ(0, (int)task_mgr->get_task_size());
229 CASE_EXPECT_EQ(0, (int)task_mgr->get_tick_checkpoint_size());
230
231 CASE_EXPECT_EQ(2, g_test_coroutine_task_manager_status);
232}
233
234CASE_TEST(coroutine_task_manager, update_timeout) {
235 typedef cotask::task<>::ptr_t task_ptr_type;
236 task_ptr_type co_task = cotask::task<>::create(test_context_task_manager_action());
237
238 typedef cotask::task_manager<cotask::task<> > mgr_t;
239 mgr_t::ptr_t task_mgr = mgr_t::create();
240
241 CASE_EXPECT_EQ(0, (int)task_mgr->get_task_size());
242 g_test_coroutine_task_manager_status = 0;
243
244 task_mgr->add_task(co_task, 10, 0);
245 CASE_EXPECT_EQ(1, (int)task_mgr->get_task_size());
246 CASE_EXPECT_EQ(1, (int)task_mgr->get_tick_checkpoint_size());
247
248 task_mgr->tick(5);
249 CASE_EXPECT_EQ(copp::COPP_EC_SUCCESS, task_mgr->set_timeout(co_task->get_id(), 20, 0));
250
251 CASE_EXPECT_EQ(1, (int)task_mgr->get_task_size());
252 CASE_EXPECT_EQ(1, (int)task_mgr->get_tick_checkpoint_size());
253
254 task_mgr->tick(24, 1);
255
256 CASE_EXPECT_EQ(1, (int)task_mgr->get_task_size());
257 CASE_EXPECT_EQ(1, (int)task_mgr->get_tick_checkpoint_size());
258
259 task_mgr->tick(25, 1);
260
261 CASE_EXPECT_EQ(0, (int)task_mgr->get_task_size());
262 CASE_EXPECT_EQ(0, (int)task_mgr->get_tick_checkpoint_size());
263
264 CASE_EXPECT_EQ(2, g_test_coroutine_task_manager_status);
265}
266
267class test_context_task_manager_action_protect_this_task : public cotask::impl::task_action_impl {
268 public:
269 int operator()(void *) override {
270 int use_count = static_cast<int>(cotask::this_task::get<cotask::task<> >()->use_count());
271 CASE_EXPECT_EQ(2, use_count);
272 cotask::this_task::get_task()->yield();
273 use_count = static_cast<int>(cotask::this_task::get<cotask::task<> >()->use_count());
274 // if we support rvalue-reference, reference may be smaller.
275 // remove action will be 3, resume and destroy will be 1 or 2
276 // CASE_EXPECT_TRUE(use_count >= 1 && use_count <= 3);
277 CASE_EXPECT_TRUE(use_count >= 1 && use_count <= 2);
278
279 ++g_test_coroutine_task_manager_status;
280 return 0;
281 }
282};
283
284CASE_TEST(coroutine_task_manager, protect_this_task) {
285 typedef cotask::task<>::ptr_t task_ptr_type;
286
287 {
288 typedef cotask::task_manager<cotask::task<> > mgr_t;
289 mgr_t::ptr_t task_mgr = mgr_t::create();
290
291 g_test_coroutine_task_manager_status = 0;
292 task_ptr_type co_task = cotask::task<>::create(test_context_task_manager_action_protect_this_task());
293 cotask::task<>::id_t id_finished = co_task->get_id();
294 task_mgr->add_task(co_task);
295
296 co_task = cotask::task<>::create(test_context_task_manager_action_protect_this_task());
297 cotask::task<>::id_t id_unfinished = co_task->get_id();
298 task_mgr->add_task(co_task);
299
300 co_task = cotask::task<>::create(test_context_task_manager_action_protect_this_task());
301 cotask::task<>::id_t id_removed = co_task->get_id();
302 task_mgr->add_task(co_task);
303
304 co_task.reset();
305
306 task_mgr->start(id_finished);
307 task_mgr->start(id_unfinished);
308 task_mgr->start(id_removed);
309
310 CASE_EXPECT_EQ(3, (int)task_mgr->get_task_size());
311 task_mgr->resume(id_finished);
312
313 CASE_EXPECT_EQ(2, (int)task_mgr->get_task_size());
314
315 task_mgr->remove_task(id_removed);
316
317 CASE_EXPECT_EQ(1, (int)task_mgr->get_task_size());
318 }
319
320 // all task should be finished
321 CASE_EXPECT_EQ(3, (int)g_test_coroutine_task_manager_status);
322}
323
324# if LIBCOPP_MACRO_ENABLE_MULTI_THREAD
325static LIBCOPP_COPP_NAMESPACE_ID::util::lock::atomic_int_type<int> g_test_coroutine_task_manager_atomic;
326
327static constexpr const int test_context_task_manager_action_mt_run_times = 10000;
328enum { test_context_task_manager_action_mt_thread_num = 1000 };
329
330struct test_context_task_manager_action_mt_thread : public cotask::impl::task_action_impl {
331 public:
332 int operator()(void *run_count_p) override {
333 assert(run_count_p);
334 int *run_count = reinterpret_cast<int *>(run_count_p);
335
336 while (*run_count < test_context_task_manager_action_mt_run_times) {
337 ++(*run_count);
338 ++g_test_coroutine_task_manager_atomic;
339
340 cotask::this_task::get_task()->yield(&run_count_p);
341
342 run_count = reinterpret_cast<int *>(run_count_p);
343 }
344 return 0;
345 }
346};
347
348struct test_context_task_manager_mt_thread_runner {
349 typedef cotask::task_manager<cotask::task<> > mgr_t;
350 int run_count;
351 mgr_t::ptr_t task_mgr;
352 test_context_task_manager_mt_thread_runner(mgr_t::ptr_t mgr) : run_count(0), task_mgr(mgr) {}
353
354 int operator()() {
355 typedef cotask::task<>::ptr_t task_ptr_type;
356
357 task_ptr_type co_task =
358 cotask::task<>::create(test_context_task_manager_action_mt_thread(), 16 * 1024); // use 16KB for stack
359 cotask::task<>::id_t task_id = co_task->get_id();
360 task_mgr->add_task(co_task);
361
362 task_mgr->start(task_id, &run_count);
363
364 while (false == co_task->is_completed()) {
365 task_mgr->resume(task_id, &run_count);
366 }
367
368 CASE_EXPECT_EQ(test_context_task_manager_action_mt_run_times, run_count);
369 return 0;
370 }
371};
372
373CASE_TEST(coroutine_task_manager, create_and_run_mt) {
374 typedef cotask::task_manager<cotask::task<> > mgr_t;
375 mgr_t::ptr_t task_mgr = mgr_t::create();
376
377 g_test_coroutine_task_manager_atomic.store(0);
378
379 std::unique_ptr<std::thread> thds[test_context_task_manager_action_mt_thread_num];
380 for (int i = 0; i < test_context_task_manager_action_mt_thread_num; ++i) {
381 thds[i].reset(new std::thread(test_context_task_manager_mt_thread_runner(task_mgr)));
382 }
383
384 for (int i = 0; i < test_context_task_manager_action_mt_thread_num; ++i) {
385 thds[i]->join();
386 }
387
388 CASE_EXPECT_EQ(test_context_task_manager_action_mt_run_times * test_context_task_manager_action_mt_thread_num,
389 g_test_coroutine_task_manager_atomic.load());
390}
391# endif
392
393# if defined(LIBCOTASK_MACRO_AUTO_CLEANUP_MANAGER) && LIBCOTASK_MACRO_AUTO_CLEANUP_MANAGER
394CASE_TEST(coroutine_task_manager, auto_cleanup_for_manager) {
395 typedef cotask::task<>::ptr_t task_ptr_type;
396 task_ptr_type co_task = cotask::task<>::create(test_context_task_manager_action());
397
398 typedef cotask::task_manager<cotask::task<> > mgr_t;
399 mgr_t::ptr_t task_mgr1 = mgr_t::create();
400 mgr_t::ptr_t task_mgr2 = mgr_t::create();
401
402 CASE_EXPECT_EQ(0, task_mgr1->add_task(co_task, 5, 0));
403 CASE_EXPECT_EQ(copp::COPP_EC_TASK_ALREADY_IN_ANOTHER_MANAGER, task_mgr2->add_task(co_task, 5, 0));
404
405 CASE_EXPECT_EQ(1, task_mgr1->get_task_size());
406 CASE_EXPECT_EQ(1, task_mgr1->get_tick_checkpoint_size());
407
408 co_task->start();
409 co_task->resume();
410
411 CASE_EXPECT_EQ(0, task_mgr1->get_task_size());
412 CASE_EXPECT_EQ(0, task_mgr1->get_tick_checkpoint_size());
413}
414# endif
415
416# if defined(LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR) && LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR
417class test_context_task_manager_action_with_exception : public cotask::impl::task_action_impl {
418 public:
419 int operator()(void *) override {
420 ++g_test_coroutine_task_manager_status;
421
422 cotask::this_task::get_task()->yield();
423
424 ++g_test_coroutine_task_manager_status;
425
426 CASE_MSG_INFO() << std::string().at(1) << std::endl;
427
428 return 0;
429 }
430};
431
432CASE_TEST(coroutine_task_manager, exception_safe) {
433 typedef cotask::task<>::ptr_t task_ptr_type;
434 task_ptr_type co_task = cotask::task<>::create(test_context_task_manager_action_with_exception());
435
436 typedef cotask::task_manager<cotask::task<> > mgr_t;
437 mgr_t::ptr_t task_mgr1 = mgr_t::create();
438 mgr_t::ptr_t task_mgr2 = mgr_t::create();
439
440 CASE_EXPECT_EQ(0, task_mgr1->add_task(co_task, 5, 0));
441 CASE_EXPECT_EQ(copp::COPP_EC_TASK_ALREADY_IN_ANOTHER_MANAGER, task_mgr2->add_task(co_task, 5, 0));
442
443 CASE_EXPECT_EQ(1, task_mgr1->get_task_size());
444 CASE_EXPECT_EQ(1, task_mgr1->get_tick_checkpoint_size());
445
446 int check_status = g_test_coroutine_task_manager_status;
447 try {
448 co_task->start();
449 co_task->resume();
450 } catch (const std::exception &e) {
451 CASE_MSG_INFO() << "Catch a exception: " << e.what() << std::endl;
452 }
453
454 CASE_EXPECT_EQ(check_status + 2, g_test_coroutine_task_manager_status);
455 CASE_EXPECT_EQ(0, task_mgr1->get_task_size());
456 CASE_EXPECT_EQ(0, task_mgr1->get_tick_checkpoint_size());
457 CASE_EXPECT_TRUE(co_task->is_completed());
458}
459# endif
460#else
461CASE_TEST(coroutine_task_manager, disabled) {}
462#endif
cotask::task_manager< my_task_t > mgr_t
my_task_t::ptr_t task_ptr_type
mgr_t::ptr_t task_mgr
#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_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