libcopp  2.2.0
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 
15 static int g_test_coroutine_task_manager_status = 0;
16 class test_context_task_manager_action : public cotask::impl::task_action_impl {
17  public:
18  int operator()(void *) {
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
24 
25  ++g_test_coroutine_task_manager_status;
26 
27  return 0;
28  }
29 };
30 
31 CASE_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 
63 CASE_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 
112 CASE_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 
175 CASE_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 
207 CASE_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 
234 CASE_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 
267 class test_context_task_manager_action_protect_this_task : public cotask::impl::task_action_impl {
268  public:
269  int operator()(void *) {
270  int use_count = static_cast<int>(cotask::this_task::get<cotask::task<> >()->use_count());
271  CASE_EXPECT_EQ(2, use_count);
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 
284 CASE_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 static LIBCOPP_COPP_NAMESPACE_ID::util::lock::atomic_int_type<int> g_test_coroutine_task_manager_atomic;
325 
326 static constexpr const int test_context_task_manager_action_mt_run_times = 10000;
327 enum { test_context_task_manager_action_mt_thread_num = 1000 };
328 
329 struct test_context_task_manager_action_mt_thread : public cotask::impl::task_action_impl {
330  public:
331  int operator()(void *run_count_p) {
332  assert(run_count_p);
333  int *run_count = reinterpret_cast<int *>(run_count_p);
334 
335  while (*run_count < test_context_task_manager_action_mt_run_times) {
336  ++(*run_count);
337  ++g_test_coroutine_task_manager_atomic;
338 
339  cotask::this_task::get_task()->yield(&run_count_p);
340 
341  run_count = reinterpret_cast<int *>(run_count_p);
342  }
343  return 0;
344  }
345 };
346 
347 struct test_context_task_manager_mt_thread_runner {
348  typedef cotask::task_manager<cotask::task<> > mgr_t;
349  int run_count;
350  mgr_t::ptr_t task_mgr;
351  test_context_task_manager_mt_thread_runner(mgr_t::ptr_t mgr) : run_count(0), task_mgr(mgr) {}
352 
353  int operator()() {
354  typedef cotask::task<>::ptr_t task_ptr_type;
355 
356  task_ptr_type co_task =
357  cotask::task<>::create(test_context_task_manager_action_mt_thread(), 16 * 1024); // use 16KB for stack
358  cotask::task<>::id_t task_id = co_task->get_id();
359  task_mgr->add_task(co_task);
360 
361  task_mgr->start(task_id, &run_count);
362 
363  while (false == co_task->is_completed()) {
364  task_mgr->resume(task_id, &run_count);
365  }
366 
367  CASE_EXPECT_EQ(test_context_task_manager_action_mt_run_times, run_count);
368  return 0;
369  }
370 };
371 
372 CASE_TEST(coroutine_task_manager, create_and_run_mt) {
373  typedef cotask::task_manager<cotask::task<> > mgr_t;
374  mgr_t::ptr_t task_mgr = mgr_t::create();
375 
376  g_test_coroutine_task_manager_atomic.store(0);
377 
378  std::unique_ptr<std::thread> thds[test_context_task_manager_action_mt_thread_num];
379  for (int i = 0; i < test_context_task_manager_action_mt_thread_num; ++i) {
380  thds[i].reset(new std::thread(test_context_task_manager_mt_thread_runner(task_mgr)));
381  }
382 
383  for (int i = 0; i < test_context_task_manager_action_mt_thread_num; ++i) {
384  thds[i]->join();
385  }
386 
387  CASE_EXPECT_EQ(test_context_task_manager_action_mt_run_times * test_context_task_manager_action_mt_thread_num,
388  g_test_coroutine_task_manager_atomic.load());
389 }
390 
391 # if defined(LIBCOTASK_MACRO_AUTO_CLEANUP_MANAGER) && LIBCOTASK_MACRO_AUTO_CLEANUP_MANAGER
392 CASE_TEST(coroutine_task_manager, auto_cleanup_for_manager) {
393  typedef cotask::task<>::ptr_t task_ptr_type;
394  task_ptr_type co_task = cotask::task<>::create(test_context_task_manager_action());
395 
396  typedef cotask::task_manager<cotask::task<> > mgr_t;
397  mgr_t::ptr_t task_mgr1 = mgr_t::create();
398  mgr_t::ptr_t task_mgr2 = mgr_t::create();
399 
400  CASE_EXPECT_EQ(0, task_mgr1->add_task(co_task, 5, 0));
401  CASE_EXPECT_EQ(copp::COPP_EC_TASK_ALREADY_IN_ANOTHER_MANAGER, task_mgr2->add_task(co_task, 5, 0));
402 
403  CASE_EXPECT_EQ(1, task_mgr1->get_task_size());
404  CASE_EXPECT_EQ(1, task_mgr1->get_tick_checkpoint_size());
405 
406  co_task->start();
407  co_task->resume();
408 
409  CASE_EXPECT_EQ(0, task_mgr1->get_task_size());
410  CASE_EXPECT_EQ(0, task_mgr1->get_tick_checkpoint_size());
411 }
412 # endif
413 
414 # if defined(LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR) && LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR
415 class test_context_task_manager_action_with_exception : public cotask::impl::task_action_impl {
416  public:
417  int operator()(void *) {
418  ++g_test_coroutine_task_manager_status;
419 
421 
422  ++g_test_coroutine_task_manager_status;
423 
424  CASE_MSG_INFO() << std::string().at(1) << std::endl;
425 
426  return 0;
427  }
428 };
429 
430 CASE_TEST(coroutine_task_manager, exception_safe) {
431  typedef cotask::task<>::ptr_t task_ptr_type;
432  task_ptr_type co_task = cotask::task<>::create(test_context_task_manager_action_with_exception());
433 
434  typedef cotask::task_manager<cotask::task<> > mgr_t;
435  mgr_t::ptr_t task_mgr1 = mgr_t::create();
436  mgr_t::ptr_t task_mgr2 = mgr_t::create();
437 
438  CASE_EXPECT_EQ(0, task_mgr1->add_task(co_task, 5, 0));
439  CASE_EXPECT_EQ(copp::COPP_EC_TASK_ALREADY_IN_ANOTHER_MANAGER, task_mgr2->add_task(co_task, 5, 0));
440 
441  CASE_EXPECT_EQ(1, task_mgr1->get_task_size());
442  CASE_EXPECT_EQ(1, task_mgr1->get_tick_checkpoint_size());
443 
444  int check_status = g_test_coroutine_task_manager_status;
445  try {
446  co_task->start();
447  co_task->resume();
448  } catch (const std::exception &e) {
449  CASE_MSG_INFO() << "Catch a exception: " << e.what() << std::endl;
450  }
451 
452  CASE_EXPECT_EQ(check_status + 2, g_test_coroutine_task_manager_status);
453  CASE_EXPECT_EQ(0, task_mgr1->get_task_size());
454  CASE_EXPECT_EQ(0, task_mgr1->get_tick_checkpoint_size());
455  CASE_EXPECT_TRUE(co_task->is_completed());
456 }
457 # endif
458 #else
459 CASE_TEST(coroutine_task_manager, disabled) {}
460 #endif
virtual int yield(void **priv_data)=0
CASE_TEST(coroutine_task_manager, disabled)
@ COPP_EC_SUCCESS
COPP_EC_SUCCESS.
Definition: errno.h:12
@ COPP_EC_TASK_IS_EXITING
COPP_EC_TASK_IS_EXITING.
Definition: errno.h:38
@ COPP_EC_TASK_ALREADY_IN_ANOTHER_MANAGER
COPP_EC_TASK_ALREADY_IN_ANOTHER_MANAGER.
Definition: errno.h:41
LIBCOPP_COTASK_API impl::task_impl * get_task() LIBCOPP_MACRO_NOEXCEPT
get current running task
Definition: this_task.cpp:9
cotask::task_manager< my_task_t > mgr_t
my_task_t::ptr_t task_ptr_type
mgr_t::ptr_t task_mgr
@ EN_TS_DONE
Definition: task_impl.h:32
@ EN_TS_TIMEOUT
Definition: task_impl.h:35
@ EN_TS_KILLED
Definition: task_impl.h:34
@ EN_TS_WAITING
Definition: task_impl.h:31
@ EN_TS_CANCELED
Definition: task_impl.h:33
#define CASE_MSG_INFO()
Definition: test_macros.h:114
#define CASE_EXPECT_FALSE(c)
Definition: test_macros.h:95
#define CASE_EXPECT_EQ(l, r)
Definition: test_macros.h:96
#define CASE_EXPECT_NE(l, r)
Definition: test_macros.h:97
#define CASE_EXPECT_TRUE(c)
Definition: test_macros.h:94