libcopp  2.2.0
task_manager.h
Go to the documentation of this file.
1 // Copyright 2023 owent
2 
3 #pragma once
4 
5 #include <libcopp/utils/config/libcopp_build_features.h>
6 
8 
9 // clang-format off
10 #include <libcopp/utils/config/stl_include_prefix.h> // NOLINT(build/include_order)
11 // clang-format on
12 #include <assert.h>
13 #include <stdint.h>
14 #include <algorithm>
15 #include <ctime>
16 #include <list>
17 #include <set>
18 #include <unordered_map>
19 #include <vector>
20 
21 #ifdef __cpp_impl_three_way_comparison
22 # include <compare>
23 #endif
24 
25 #if defined(LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR) && LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR
26 # include <exception>
27 #endif
28 // clang-format off
29 #include <libcopp/utils/config/stl_include_suffix.h> // NOLINT(build/include_order)
30 // clang-format on
31 
32 #include "libcotask/task.h"
33 #include "libcotask/task_promise.h"
34 
35 LIBCOPP_COTASK_NAMESPACE_BEGIN
36 
37 namespace detail {
38 struct LIBCOPP_COTASK_API_HEAD_ONLY tickspec_t {
39  time_t tv_sec; /* Seconds. */
40  int tv_nsec; /* Nanoseconds. */
41 
42  inline friend bool operator==(const tickspec_t &l, const tickspec_t &r) {
43  return l.tv_sec == r.tv_sec && l.tv_nsec == r.tv_nsec;
44  }
45 
46 #ifdef __cpp_impl_three_way_comparison
47  inline friend std::strong_ordering operator<=>(const tickspec_t &l, const tickspec_t &r) {
48  return (l.tv_sec != r.tv_sec) ? l.tv_sec <=> r.tv_sec : l.tv_nsec <=> r.tv_nsec;
49  }
50 #else
51  inline friend bool operator!=(const tickspec_t &l, const tickspec_t &r) {
52  return l.tv_sec != r.tv_sec || l.tv_nsec != r.tv_nsec;
53  }
54 
55  inline friend bool operator<(const tickspec_t &l, const tickspec_t &r) {
56  return (l.tv_sec != r.tv_sec) ? l.tv_sec < r.tv_sec : l.tv_nsec < r.tv_nsec;
57  }
58 
59  inline friend bool operator<=(const tickspec_t &l, const tickspec_t &r) {
60  return (l.tv_sec != r.tv_sec) ? l.tv_sec <= r.tv_sec : l.tv_nsec <= r.tv_nsec;
61  }
62 
63  inline friend bool operator>(const tickspec_t &l, const tickspec_t &r) {
64  return (l.tv_sec != r.tv_sec) ? l.tv_sec > r.tv_sec : l.tv_nsec > r.tv_nsec;
65  }
66 
67  inline friend bool operator>=(const tickspec_t &l, const tickspec_t &r) {
68  return (l.tv_sec != r.tv_sec) ? l.tv_sec >= r.tv_sec : l.tv_nsec >= r.tv_nsec;
69  }
70 #endif
71 };
72 
73 template <class TTASK_ID_TYPE>
74 struct LIBCOPP_COTASK_API_HEAD_ONLY task_timer_node {
76  TTASK_ID_TYPE task_id;
77 
78  inline friend bool operator==(const task_timer_node &l, const task_timer_node &r) {
79  return l.expired_time == r.expired_time && l.task_id == r.task_id;
80  }
81 
82 #ifdef __cpp_impl_three_way_comparison
83  inline friend std::strong_ordering operator<=>(const task_timer_node &l, const task_timer_node &r) {
84  if (l.expired_time != r.expired_time) {
85  return l.expired_time <=> r.expired_time;
86  }
87 
88  return l.task_id <=> r.task_id;
89  }
90 #else
91  inline friend bool operator!=(const task_timer_node &l, const task_timer_node &r) {
92  return l.expired_time != r.expired_time || l.task_id != r.task_id;
93  }
94 
95  inline friend bool operator<(const task_timer_node &l, const task_timer_node &r) {
96  if (l.expired_time != r.expired_time) {
97  return l.expired_time < r.expired_time;
98  }
99 
100  return l.task_id < r.task_id;
101  }
102 
103  inline friend bool operator<=(const task_timer_node &l, const task_timer_node &r) {
104  if (l.expired_time != r.expired_time) {
105  return l.expired_time <= r.expired_time;
106  }
107 
108  return l.task_id <= r.task_id;
109  }
110 
111  inline friend bool operator>(const task_timer_node &l, const task_timer_node &r) {
112  if (l.expired_time != r.expired_time) {
113  return l.expired_time > r.expired_time;
114  }
115 
116  return l.task_id > r.task_id;
117  }
118 
119  inline friend bool operator>=(const task_timer_node &l, const task_timer_node &r) {
120  if (l.expired_time != r.expired_time) {
121  return l.expired_time >= r.expired_time;
122  }
123 
124  return l.task_id >= r.task_id;
125  }
126 #endif
127 };
128 
129 template <class TTask>
130 struct LIBCOPP_COTASK_API_HEAD_ONLY task_manager_node;
131 
132 template <class TCO_MACRO>
133 struct LIBCOPP_COTASK_API_HEAD_ONLY task_manager_node<task<TCO_MACRO>> {
135 
137  typename std::set<task_timer_node<typename task<TCO_MACRO>::id_type>>::iterator timer_node;
138 };
139 
140 #if defined(LIBCOPP_MACRO_ENABLE_STD_COROUTINE) && LIBCOPP_MACRO_ENABLE_STD_COROUTINE
141 template <class TVALUE, class TPRIVATE_DATA, class TERROR_TRANSFORM>
142 struct LIBCOPP_COTASK_API_HEAD_ONLY task_manager_node<task_future<TVALUE, TPRIVATE_DATA, TERROR_TRANSFORM>> {
143  using task_type = task_future<TVALUE, TPRIVATE_DATA, TERROR_TRANSFORM>;
144 
145  task_type task_;
146  typename std::set<task_timer_node<typename task_type::id_type>>::iterator timer_node;
147 };
148 #endif
149 
150 } // namespace detail
151 
152 template <typename TTask>
153 class LIBCOPP_COTASK_API_HEAD_ONLY task_manager;
154 
158 template <typename TCO_MACRO>
159 class LIBCOPP_COTASK_API_HEAD_ONLY task_manager<task<TCO_MACRO>> {
160  public:
162  using container_type = std::unordered_map<typename task_type::id_type, detail::task_manager_node<task_type>>;
163  using id_type = typename task_type::id_type;
165  using self_type = task_manager<task_type>;
166  using ptr_type = std::shared_ptr<self_type>;
167 
168  struct flag_type {
169  enum type {
170  EN_TM_NONE = 0x00,
171  EN_TM_IN_TICK = 0x01,
172  EN_TM_IN_RESET = 0x02,
173  };
174  };
175 
176  // Compability with libcopp-1.x
177  using id_t = id_type;
178  using self_t = self_type;
179  using task_t = task_type;
181  using ptr_t = ptr_type;
183  using flag_t = flag_type;
184 
185  private:
186  struct flag_guard_type {
187  int *data_;
189  inline flag_guard_type(int *flags, typename flag_type::type v) : data_(flags), flag_(v) {
190  if (nullptr == data_ || (*data_ & flag_)) {
191  flag_ = flag_type::EN_TM_NONE;
192  data_ = nullptr;
193  } else {
194  (*data_) |= flag_;
195  }
196  }
197  inline ~flag_guard_type() {
198  if (*this) {
199  (*data_) &= ~flag_;
200  }
201  }
202 
203  inline operator bool() { return nullptr != data_ && flag_type::EN_TM_NONE != flag_; }
204  };
205 
206  public:
207  task_manager() : flags_(0) {
208  last_tick_time_.tv_sec = 0;
209  last_tick_time_.tv_nsec = 0;
210  }
211 
213  // safe remove all task
214  reset();
215  }
216 
217  void reset() {
218  flag_guard_type reset_flag(&flags_, flag_type::EN_TM_IN_RESET);
219  if (!reset_flag) {
220  return;
221  }
222 
223  std::vector<task_ptr_type> all_tasks;
224  // first, lock and reset all data
225  {
226 #if !defined(LIBCOPP_DISABLE_ATOMIC_LOCK) || !(LIBCOPP_DISABLE_ATOMIC_LOCK)
227  LIBCOPP_COPP_NAMESPACE_ID::util::lock::lock_holder<LIBCOPP_COPP_NAMESPACE_ID::util::lock::spin_lock> lock_guard{
228  action_lock_};
229 #endif
230 
231  for (typename container_type::iterator iter = tasks_.begin(); iter != tasks_.end(); ++iter) {
232  all_tasks.push_back(iter->second.task_);
233  remove_timeout_timer(iter->second);
234  }
235 
236  tasks_.clear();
237  task_timeout_timer_.clear();
238  flags_ = 0;
239  last_tick_time_.tv_sec = 0;
240  last_tick_time_.tv_nsec = 0;
241  }
242 
243  // then, kill all tasks
244  for (typename std::vector<task_ptr_type>::iterator iter = all_tasks.begin(); iter != all_tasks.end(); ++iter) {
245  if (!(*iter)->is_exiting()) {
246  (*iter)->kill(EN_TS_KILLED);
247  }
248  }
249  }
250 
255  static ptr_type create() { return std::make_shared<self_type>(); }
256 
270  int add_task(const task_ptr_type &task, time_t timeout_sec, int timeout_nsec) {
271  if (!task) {
272  assert(task);
274  }
275 
276  if (flags_ & flag_type::EN_TM_IN_RESET) {
278  }
279 
280  if (task->is_exiting()) {
282  }
283 
284  // try to cast type
285  using pair_type = typename container_type::value_type;
286  detail::task_manager_node<task_type> task_node;
287  task_node.task_ = task;
288  task_node.timer_node = task_timeout_timer_.end();
289 
290  if (!task_node.task_) {
291  assert(task_node.task_);
293  }
294 
295  // lock before we will operator tasks_
296 #if !defined(LIBCOPP_DISABLE_ATOMIC_LOCK) || !(LIBCOPP_DISABLE_ATOMIC_LOCK)
297  LIBCOPP_COPP_NAMESPACE_ID::util::lock::lock_holder<LIBCOPP_COPP_NAMESPACE_ID::util::lock::spin_lock> lock_guard{
298  action_lock_};
299 #endif
300 
301  id_type task_id = task->get_id();
302  if (tasks_.end() != tasks_.find(task_id)) {
304  }
305 
306 #if defined(LIBCOTASK_MACRO_AUTO_CLEANUP_MANAGER) && LIBCOTASK_MACRO_AUTO_CLEANUP_MANAGER
307  using task_manager_helper = typename task_type::task_manager_helper;
308  if (!task_manager_helper::setup_task_manager(*task, reinterpret_cast<void *>(this), &task_cleanup_callback)) {
310  }
311 #endif
312 
313  // try to insert to container
314  std::pair<typename container_type::iterator, bool> res = tasks_.insert(pair_type(task_id, task_node));
315  if (false == res.second) {
316 #if defined(LIBCOTASK_MACRO_AUTO_CLEANUP_MANAGER) && LIBCOTASK_MACRO_AUTO_CLEANUP_MANAGER
317  task_manager_helper::cleanup_task_manager(*task, reinterpret_cast<void *>(this));
318 #endif
320  }
321 
322  // add timeout controller
323  set_timeout_timer(res.first->second, timeout_sec, timeout_nsec);
325  }
326 
335  int add_task(const task_ptr_type &task) { return add_task(task, 0, 0); }
336 
351  int set_timeout(id_type id, time_t timeout_sec, int timeout_nsec) {
352  if (flags_ & flag_type::EN_TM_IN_RESET) {
354  }
355 
356  {
357 #if !defined(LIBCOPP_DISABLE_ATOMIC_LOCK) || !(LIBCOPP_DISABLE_ATOMIC_LOCK)
358  LIBCOPP_COPP_NAMESPACE_ID::util::lock::lock_holder<LIBCOPP_COPP_NAMESPACE_ID::util::lock::spin_lock> lock_guard{
359  action_lock_};
360 #endif
361 
362  using iter_type = typename container_type::iterator;
363  iter_type iter = tasks_.find(id);
364  if (tasks_.end() == iter) return LIBCOPP_COPP_NAMESPACE_ID::COPP_EC_NOT_FOUND;
365 
366  set_timeout_timer(iter->second, timeout_sec, timeout_nsec);
367  }
368 
370  }
371 
378  inline int remove_task(id_type id, const task_ptr_type &confirm_ptr) { return remove_task(id, confirm_ptr.get()); }
379 
385  inline int remove_task(id_type id) { return remove_task(id, nullptr); }
386 
393  int remove_task(id_type id, const task_type *confirm_ptr) {
394  if (flags_ & flag_type::EN_TM_IN_RESET) {
396  }
397 
398  task_ptr_type task_inst;
399  {
400 #if !defined(LIBCOPP_DISABLE_ATOMIC_LOCK) || !(LIBCOPP_DISABLE_ATOMIC_LOCK)
401  LIBCOPP_COPP_NAMESPACE_ID::util::lock::lock_holder<LIBCOPP_COPP_NAMESPACE_ID::util::lock::spin_lock> lock_guard{
402  action_lock_};
403 #endif
404 
405  using iter_type = typename container_type::iterator;
406  iter_type iter = tasks_.find(id);
407  if (tasks_.end() == iter) {
409  }
410  if (nullptr != confirm_ptr && iter->second.task_.get() != confirm_ptr) {
412  }
413 
414  // make sure running task be killed first
415  task_inst = std::move(iter->second.task_);
416 
417  remove_timeout_timer(iter->second);
418  tasks_.erase(iter);
419  }
420 
421  if (task_inst) {
422 #if defined(LIBCOTASK_MACRO_AUTO_CLEANUP_MANAGER) && LIBCOTASK_MACRO_AUTO_CLEANUP_MANAGER
423  using task_manager_helper = typename task_type::task_manager_helper;
424  // already cleanup, there is no need to cleanup again
425  task_manager_helper::cleanup_task_manager(*task_inst, reinterpret_cast<void *>(this));
426 #endif
427 
428  EN_TASK_STATUS task_status = task_inst->get_status();
429  if (task_status > EN_TS_CREATED && task_status < EN_TS_DONE) {
430  return task_inst->kill(EN_TS_KILLED, nullptr);
431  }
432  }
433 
435  }
436 
443  if (flags_ & flag_type::EN_TM_IN_RESET) {
444  return task_ptr_type();
445  }
446 
447 #if !defined(LIBCOPP_DISABLE_ATOMIC_LOCK) || !(LIBCOPP_DISABLE_ATOMIC_LOCK)
448  LIBCOPP_COPP_NAMESPACE_ID::util::lock::lock_holder<LIBCOPP_COPP_NAMESPACE_ID::util::lock::spin_lock> lock_guard{
449  action_lock_};
450 #endif
451 
452  using iter_type = typename container_type::iterator;
453  iter_type iter = tasks_.find(id);
454  if (tasks_.end() == iter) return task_ptr_type();
455 
456  return iter->second.task_;
457  }
458 
459  // int add_scheduler();
460  // int scheduling_once();
461  // int scheduling_loop();
462 #if defined(LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR) && LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR
463  int start(id_type id, void *priv_data = nullptr) {
464  std::list<std::exception_ptr> eptrs;
465  int ret = start(id, eptrs, priv_data);
466  task_type::maybe_rethrow(eptrs);
467  return ret;
468  }
469 
470  int start(id_type id, std::list<std::exception_ptr> &unhandled, void *priv_data = nullptr) LIBCOPP_MACRO_NOEXCEPT {
471 #else
472  int start(id_type id, void *priv_data = nullptr) {
473 #endif
474  if (flags_ & flag_type::EN_TM_IN_RESET) {
476  }
477 
478  task_ptr_type task_inst;
479  {
480 #if !defined(LIBCOPP_DISABLE_ATOMIC_LOCK) || !(LIBCOPP_DISABLE_ATOMIC_LOCK)
481  LIBCOPP_COPP_NAMESPACE_ID::util::lock::lock_holder<LIBCOPP_COPP_NAMESPACE_ID::util::lock::spin_lock> lock_guard{
482  action_lock_};
483 #endif
484 
485  using iter_type = typename container_type::iterator;
486  iter_type iter = tasks_.find(id);
487  if (tasks_.end() == iter) return LIBCOPP_COPP_NAMESPACE_ID::COPP_EC_NOT_FOUND;
488 
489  task_inst = iter->second.task_;
490  }
491 
492  // unlock and then run start
493  if (task_inst) {
494 #if defined(LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR) && LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR
495  int ret = task_inst->start(unhandled, priv_data);
496 #else
497  int ret = task_inst->start(priv_data);
498 #endif
499 
500  // if task is finished, remove it
501  if (task_inst->get_status() >= EN_TS_DONE) {
502  remove_task(id);
503  }
504 
505  return ret;
506  } else {
508  }
509  }
510 
511 #if defined(LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR) && LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR
512  int resume(id_type id, void *priv_data = nullptr) {
513  std::list<std::exception_ptr> eptrs;
514  int ret = resume(id, eptrs, priv_data);
515  task_type::maybe_rethrow(eptrs);
516  return ret;
517  }
518 
519  int resume(id_type id, std::list<std::exception_ptr> &unhandled, void *priv_data = nullptr) LIBCOPP_MACRO_NOEXCEPT {
520 #else
521  int resume(id_type id, void *priv_data = nullptr) {
522 #endif
523  if (flags_ & flag_type::EN_TM_IN_RESET) {
525  }
526 
527  task_ptr_type task_inst;
528  {
529 #if !defined(LIBCOPP_DISABLE_ATOMIC_LOCK) || !(LIBCOPP_DISABLE_ATOMIC_LOCK)
530  LIBCOPP_COPP_NAMESPACE_ID::util::lock::lock_holder<LIBCOPP_COPP_NAMESPACE_ID::util::lock::spin_lock> lock_guard{
531  action_lock_};
532 #endif
533 
534  using iter_type = typename container_type::iterator;
535  iter_type iter = tasks_.find(id);
536  if (tasks_.end() == iter) return LIBCOPP_COPP_NAMESPACE_ID::COPP_EC_NOT_FOUND;
537 
538  task_inst = iter->second.task_;
539  }
540 
541  // unlock and then run resume
542  if (task_inst) {
543 #if defined(LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR) && LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR
544  int ret = task_inst->resume(unhandled, priv_data);
545 #else
546  int ret = task_inst->resume(priv_data);
547 #endif
548 
549  // if task is finished, remove it
550  if (task_inst->get_status() >= EN_TS_DONE) {
551  remove_task(id);
552  }
553 
554  return ret;
555  } else {
557  }
558  }
559 
560 #if defined(LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR) && LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR
561  int cancel(id_type id, void *priv_data = nullptr) {
562  std::list<std::exception_ptr> eptrs;
563  int ret = cancel(id, eptrs, priv_data);
564  task_type::maybe_rethrow(eptrs);
565  return ret;
566  }
567 
568  int cancel(id_type id, std::list<std::exception_ptr> &unhandled, void *priv_data = nullptr) LIBCOPP_MACRO_NOEXCEPT {
569 #else
570  int cancel(id_type id, void *priv_data = nullptr) {
571 #endif
572  if (flags_ & flag_type::EN_TM_IN_RESET) {
574  }
575 
576  task_ptr_type task_inst;
577  {
578 #if !defined(LIBCOPP_DISABLE_ATOMIC_LOCK) || !(LIBCOPP_DISABLE_ATOMIC_LOCK)
579  LIBCOPP_COPP_NAMESPACE_ID::util::lock::lock_holder<LIBCOPP_COPP_NAMESPACE_ID::util::lock::spin_lock> lock_guard{
580  action_lock_};
581 #endif
582 
583  using iter_type = typename container_type::iterator;
584  iter_type iter = tasks_.find(id);
585  if (tasks_.end() == iter) {
587  }
588 
589  task_inst = std::move(iter->second.task_);
590 
591  remove_timeout_timer(iter->second);
592  tasks_.erase(iter); // remove from container
593  }
594 
595  // unlock and then run cancel
596  if (task_inst) {
597 #if defined(LIBCOTASK_MACRO_AUTO_CLEANUP_MANAGER) && LIBCOTASK_MACRO_AUTO_CLEANUP_MANAGER
598  using task_manager_helper = typename task_type::task_manager_helper;
599  // already cleanup, there is no need to cleanup again
600  task_manager_helper::cleanup_task_manager(*task_inst, reinterpret_cast<void *>(this));
601 #endif
602 #if defined(LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR) && LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR
603  return task_inst->cancel(unhandled, priv_data);
604 #else
605  return task_inst->cancel(priv_data);
606 #endif
607  } else {
609  }
610  }
611 
612 #if defined(LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR) && LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR
613  int kill(id_type id, enum EN_TASK_STATUS status, void *priv_data = nullptr) {
614  std::list<std::exception_ptr> eptrs;
615  int ret = kill(id, eptrs, status, priv_data);
616  task_type::maybe_rethrow(eptrs);
617  return ret;
618  }
619 
620  int kill(id_type id, std::list<std::exception_ptr> &unhandled, enum EN_TASK_STATUS status,
621  void *priv_data = nullptr) LIBCOPP_MACRO_NOEXCEPT {
622 #else
623  int kill(id_type id, enum EN_TASK_STATUS status, void *priv_data = nullptr) {
624 #endif
625  if (flags_ & flag_type::EN_TM_IN_RESET) {
627  }
628 
629  task_ptr_type task_inst;
630  {
631 #if !defined(LIBCOPP_DISABLE_ATOMIC_LOCK) || !(LIBCOPP_DISABLE_ATOMIC_LOCK)
632  LIBCOPP_COPP_NAMESPACE_ID::util::lock::lock_holder<LIBCOPP_COPP_NAMESPACE_ID::util::lock::spin_lock> lock_guard{
633  action_lock_};
634 #endif
635 
636  using iter_type = typename container_type::iterator;
637  iter_type iter = tasks_.find(id);
638  if (tasks_.end() == iter) {
640  }
641 
642  task_inst = std::move(iter->second.task_);
643 
644  remove_timeout_timer(iter->second);
645  tasks_.erase(iter); // remove from container
646  }
647 
648  // unlock and then run kill
649  if (task_inst) {
650 #if defined(LIBCOTASK_MACRO_AUTO_CLEANUP_MANAGER) && LIBCOTASK_MACRO_AUTO_CLEANUP_MANAGER
651  using task_manager_helper = typename task_type::task_manager_helper;
652  // already cleanup, there is no need to cleanup again
653  task_manager_helper::cleanup_task_manager(*task_inst, reinterpret_cast<void *>(this));
654 #endif
655 #if defined(LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR) && LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR
656  return task_inst->kill(unhandled, status, priv_data);
657 #else
658  return task_inst->kill(status, priv_data);
659 #endif
660  } else {
662  }
663  }
664 
665  int kill(id_type id, void *priv_data = nullptr) { return kill(id, EN_TS_KILLED, priv_data); }
666 
675  int tick(time_t sec, int nsec = 0) {
676  detail::tickspec_t now_tick_time;
677  // time can not be back
678  if (sec < last_tick_time_.tv_sec || (sec == last_tick_time_.tv_sec && nsec <= last_tick_time_.tv_nsec)) {
679  return 0;
680  }
681 
682  now_tick_time.tv_sec = sec;
683  now_tick_time.tv_nsec = nsec;
684 
685  // we will ignore tick when in a recursive call
686  flag_guard_type tick_flag(&flags_, flag_type::EN_TM_IN_TICK);
687  if (!tick_flag) {
689  }
690 
691  if (flags_ & flag_type::EN_TM_IN_RESET) {
693  }
694 
695  // first tick, init and reset task timeout
696  if (0 == last_tick_time_.tv_sec && 0 == last_tick_time_.tv_nsec) {
697  // hold lock
698 #if !defined(LIBCOPP_DISABLE_ATOMIC_LOCK) || !(LIBCOPP_DISABLE_ATOMIC_LOCK)
699  LIBCOPP_COPP_NAMESPACE_ID::util::lock::lock_holder<LIBCOPP_COPP_NAMESPACE_ID::util::lock::spin_lock> lock_guard{
700  action_lock_};
701 #endif
702 
703  std::set<detail::task_timer_node<id_type>> real_checkpoints;
704  for (typename std::set<detail::task_timer_node<id_type>>::iterator iter = task_timeout_timer_.begin();
705  task_timeout_timer_.end() != iter; ++iter) {
706  detail::task_timer_node<id_type> new_checkpoint = (*iter);
707  new_checkpoint.expired_time.tv_sec += sec;
708  new_checkpoint.expired_time.tv_nsec += nsec;
709  real_checkpoints.insert(new_checkpoint);
710  }
711 
712  task_timeout_timer_.swap(real_checkpoints);
713  for (typename std::set<detail::task_timer_node<id_type>>::iterator iter = task_timeout_timer_.begin();
714  task_timeout_timer_.end() != iter; ++iter) {
715  const typename std::set<detail::task_timer_node<id_type>>::value_type &checkpoint = *iter;
716  using co_iter_type = typename container_type::iterator;
717  co_iter_type co_iter = tasks_.find(checkpoint.task_id);
718 
719  if (tasks_.end() != co_iter) {
720  co_iter->second.timer_node = iter;
721  }
722  }
723  last_tick_time_ = now_tick_time;
725  }
726 
727 #if defined(LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR) && LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR
728  std::list<std::exception_ptr> eptrs;
729 #endif
730  // remove timeout tasks
731  while (false == task_timeout_timer_.empty()) {
732  task_ptr_type task_inst;
733 
734  {
735  // hold lock
736 #if !defined(LIBCOPP_DISABLE_ATOMIC_LOCK) || !(LIBCOPP_DISABLE_ATOMIC_LOCK)
737  LIBCOPP_COPP_NAMESPACE_ID::util::lock::lock_holder<LIBCOPP_COPP_NAMESPACE_ID::util::lock::spin_lock> lock_guard{
738  action_lock_};
739 #endif
740 
741  const typename std::set<detail::task_timer_node<id_type>>::value_type &timer_node =
742  *task_timeout_timer_.begin();
743  // all tasks those expired time less than now are timeout
744  if (now_tick_time <= timer_node.expired_time) {
745  break;
746  }
747 
748  // check expire time(may be changed)
749  using iter_type = typename container_type::iterator;
750 
751  iter_type iter = tasks_.find(timer_node.task_id);
752 
753  if (tasks_.end() != iter) {
754  // task may be removed before
755  task_inst = std::move(iter->second.task_);
756 
757  remove_timeout_timer(iter->second);
758  tasks_.erase(iter); // remove from container
759  }
760  }
761 
762  // task call can not be used when lock is on
763  if (task_inst && !task_inst->is_exiting()) {
764 #if defined(LIBCOTASK_MACRO_AUTO_CLEANUP_MANAGER) && LIBCOTASK_MACRO_AUTO_CLEANUP_MANAGER
765  using task_manager_helper = typename task_type::task_manager_helper;
766  // already cleanup, there is no need to cleanup again
767  task_manager_helper::cleanup_task_manager(*task_inst, reinterpret_cast<void *>(this));
768 #endif
769 #if defined(LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR) && LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR
770  task_inst->kill(eptrs, EN_TS_TIMEOUT, nullptr);
771 #else
772  task_inst->kill(EN_TS_TIMEOUT);
773 #endif
774  }
775  }
776 
777  last_tick_time_ = now_tick_time;
778 
779 #if defined(LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR) && LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR
780  task_type::maybe_rethrow(eptrs);
781 #endif
783  }
784 
789  size_t get_tick_checkpoint_size() const LIBCOPP_MACRO_NOEXCEPT { return task_timeout_timer_.size(); }
790 
795  size_t get_task_size() const LIBCOPP_MACRO_NOEXCEPT { return tasks_.size(); }
796 
801  detail::tickspec_t get_last_tick_time() const LIBCOPP_MACRO_NOEXCEPT { return last_tick_time_; }
802 
807  inline const container_type &get_container() const LIBCOPP_MACRO_NOEXCEPT { return tasks_; }
808 
813  inline const std::set<detail::task_timer_node<id_type>> &get_checkpoints() const LIBCOPP_MACRO_NOEXCEPT {
814  return task_timeout_timer_;
815  }
816 
817  private:
818  void set_timeout_timer(detail::task_manager_node<task_type> &node, time_t timeout_sec, int timeout_nsec) {
819  remove_timeout_timer(node);
820 
821  if (timeout_sec <= 0 && timeout_nsec <= 0) {
822  return;
823  }
824 
825  if (!node.task_) {
826  return;
827  }
828 
830  timer_node.task_id = node.task_->get_id();
831  timer_node.expired_time.tv_sec = last_tick_time_.tv_sec + timeout_sec;
832  timer_node.expired_time.tv_nsec = last_tick_time_.tv_nsec + timeout_nsec;
833 
834  std::pair<typename std::set<detail::task_timer_node<id_type>>::iterator, bool> res =
835  task_timeout_timer_.insert(timer_node);
836  if (res.second) {
837  node.timer_node = res.first;
838  }
839  }
840 
841  void remove_timeout_timer(detail::task_manager_node<task_type> &node) {
842  if (node.timer_node != task_timeout_timer_.end()) {
843  task_timeout_timer_.erase(node.timer_node);
844  node.timer_node = task_timeout_timer_.end();
845  }
846  }
847 
848 #if defined(LIBCOTASK_MACRO_AUTO_CLEANUP_MANAGER) && LIBCOTASK_MACRO_AUTO_CLEANUP_MANAGER
849  static void task_cleanup_callback(void *self_ptr, task_type &task_inst) {
850  if (nullptr == self_ptr) {
851  return;
852  }
853 
854  reinterpret_cast<self_type *>(self_ptr)->remove_task(task_inst.get_id(), &task_inst);
855  }
856 #endif
857 
858  private:
861  std::set<detail::task_timer_node<id_type>> task_timeout_timer_;
862 
863 #if !defined(LIBCOPP_DISABLE_ATOMIC_LOCK) || !(LIBCOPP_DISABLE_ATOMIC_LOCK)
864  LIBCOPP_COPP_NAMESPACE_ID::util::lock::spin_lock action_lock_;
865 #endif
866  int flags_;
867 };
868 
869 #if defined(LIBCOPP_MACRO_ENABLE_STD_COROUTINE) && LIBCOPP_MACRO_ENABLE_STD_COROUTINE
873 template <class TVALUE, class TPRIVATE_DATA, class TERROR_TRANSFORM>
874 class LIBCOPP_COTASK_API_HEAD_ONLY task_manager<task_future<TVALUE, TPRIVATE_DATA, TERROR_TRANSFORM>> {
875  public:
876  using task_type = task_future<TVALUE, TPRIVATE_DATA, TERROR_TRANSFORM>;
877  using container_type = std::unordered_map<typename task_type::id_type, detail::task_manager_node<task_type>>;
878  using id_type = typename task_type::id_type;
879  using task_status_type = typename task_type::task_status_type;
880  using self_type = task_manager<task_type>;
881  using ptr_type = std::shared_ptr<self_type>;
882 
883  enum class flag_type : uint32_t{
884  kNone = 0,
885  kTimerTick = 0x01,
886  kTimerReset = 0x02,
887  };
888 
889  private:
890  struct flag_guard_type {
891  uint32_t *data_;
892  flag_type flag_;
893  inline flag_guard_type(uint32_t *flags, flag_type v) : data_(flags), flag_(v) {
894  if (nullptr == data_ || (*data_ & static_cast<uint32_t>(flag_))) {
895  flag_ = flag_type::kNone;
896  data_ = nullptr;
897  } else {
898  (*data_) |= static_cast<uint32_t>(flag_);
899  }
900  }
901  inline ~flag_guard_type() {
902  if (*this) {
903  (*data_) &= ~static_cast<uint32_t>(flag_);
904  }
905  }
906 
907  inline operator bool() { return nullptr != data_ && flag_type::kNone != flag_; }
908  };
909 
910  public:
911  task_manager() : flags_(0) {
912  last_tick_time_.tv_sec = 0;
913  last_tick_time_.tv_nsec = 0;
914  }
915 
916  ~task_manager() {
917  // safe remove all task
918  reset();
919  }
920 
921  void reset() {
922  flag_guard_type reset_flag(&flags_, flag_type::kTimerReset);
923  if (!reset_flag) {
924  return;
925  }
926 
927  std::vector<task_type> all_tasks;
928  // first, lock and reset all data
929  {
930 # if !defined(LIBCOPP_DISABLE_ATOMIC_LOCK) || !(LIBCOPP_DISABLE_ATOMIC_LOCK)
931  LIBCOPP_COPP_NAMESPACE_ID::util::lock::lock_holder<LIBCOPP_COPP_NAMESPACE_ID::util::lock::spin_lock> lock_guard{
932  action_lock_};
933 # endif
934 
935  for (typename container_type::iterator iter = tasks_.begin(); iter != tasks_.end(); ++iter) {
936  all_tasks.push_back(iter->second.task_);
937  remove_timeout_timer(iter->second);
938  }
939 
940  tasks_.clear();
941  task_timeout_timer_.clear();
942  flags_ = 0;
943  last_tick_time_.tv_sec = 0;
944  last_tick_time_.tv_nsec = 0;
945  }
946 
947  // then, kill all tasks
948  for (typename std::vector<task_type>::iterator iter = all_tasks.begin(); iter != all_tasks.end(); ++iter) {
949  if (!(*iter).is_exiting()) {
950 # if defined(LIBCOTASK_MACRO_AUTO_CLEANUP_MANAGER) && LIBCOTASK_MACRO_AUTO_CLEANUP_MANAGER
951  using task_manager_helper = typename task_type::task_manager_helper;
952  // already cleanup, there is no need to cleanup again
953  task_manager_helper::cleanup_task_manager(*(*iter).get_context(), reinterpret_cast<void *>(this));
954 # endif
955 
956  (*iter).kill(task_status_type::kKilled);
957  }
958  }
959  }
960 
965  static ptr_type create() noexcept { return std::make_shared<self_type>(); }
966 
980  int add_task(const task_type &task, time_t timeout_sec, int timeout_nsec) noexcept {
981  if (flags_ & static_cast<uint32_t>(flag_type::kTimerReset)) {
983  }
984 
985  if (task.is_exiting()) {
987  }
988 
989  // try to cast type
990  using pair_type = typename container_type::value_type;
991  detail::task_manager_node<task_type> task_node;
992  task_node.task_ = task;
993  task_node.timer_node = task_timeout_timer_.end();
994 
995  // lock before we will operator tasks_
996 # if !defined(LIBCOPP_DISABLE_ATOMIC_LOCK) || !(LIBCOPP_DISABLE_ATOMIC_LOCK)
997  LIBCOPP_COPP_NAMESPACE_ID::util::lock::lock_holder<LIBCOPP_COPP_NAMESPACE_ID::util::lock::spin_lock> lock_guard{
998  action_lock_};
999 # endif
1000 
1001  id_type task_id = task.get_id();
1002  if (tasks_.end() != tasks_.find(task_id)) {
1004  }
1005 
1006 # if defined(LIBCOTASK_MACRO_AUTO_CLEANUP_MANAGER) && LIBCOTASK_MACRO_AUTO_CLEANUP_MANAGER
1007  using task_manager_helper = typename task_type::task_manager_helper;
1008  if (!task_manager_helper::setup_task_manager(*task.get_context(), reinterpret_cast<void *>(this),
1009  &task_cleanup_callback)) {
1011  }
1012 # endif
1013 
1014  // try to insert to container
1015  std::pair<typename container_type::iterator, bool> res = tasks_.insert(pair_type(task_id, task_node));
1016  if (false == res.second) {
1017 # if defined(LIBCOTASK_MACRO_AUTO_CLEANUP_MANAGER) && LIBCOTASK_MACRO_AUTO_CLEANUP_MANAGER
1018  task_manager_helper::cleanup_task_manager(*task.get_context(), reinterpret_cast<void *>(this));
1019 # endif
1021  }
1022 
1023  // add timeout controller
1024  set_timeout_timer(res.first->second, timeout_sec, timeout_nsec);
1026  }
1027 
1036  int add_task(const task_type &task) noexcept { return add_task(task, 0, 0); }
1037 
1052  int set_timeout(id_type id, time_t timeout_sec, int timeout_nsec) noexcept {
1053  if (flags_ & static_cast<uint32_t>(flag_type::kTimerReset)) {
1055  }
1056 
1057  {
1058 # if !defined(LIBCOPP_DISABLE_ATOMIC_LOCK) || !(LIBCOPP_DISABLE_ATOMIC_LOCK)
1059  LIBCOPP_COPP_NAMESPACE_ID::util::lock::lock_holder<LIBCOPP_COPP_NAMESPACE_ID::util::lock::spin_lock> lock_guard{
1060  action_lock_};
1061 # endif
1062 
1063  using iter_type = typename container_type::iterator;
1064  iter_type iter = tasks_.find(id);
1065  if (tasks_.end() == iter) return LIBCOPP_COPP_NAMESPACE_ID::COPP_EC_NOT_FOUND;
1066 
1067  set_timeout_timer(iter->second, timeout_sec, timeout_nsec);
1068  }
1069 
1071  }
1072 
1079  inline int remove_task(id_type id, const task_type &confirm_task) {
1080  return remove_task(id, confirm_task.get_context().get());
1081  }
1082 
1088  inline int remove_task(id_type id) { return remove_task(id, nullptr); }
1089 
1096  int remove_task(id_type id, const task_context_base<TVALUE> *confirm_context) {
1097  if (flags_ & static_cast<uint32_t>(flag_type::kTimerReset)) {
1099  }
1100 
1101  task_type task_inst;
1102  {
1103 # if !defined(LIBCOPP_DISABLE_ATOMIC_LOCK) || !(LIBCOPP_DISABLE_ATOMIC_LOCK)
1104  LIBCOPP_COPP_NAMESPACE_ID::util::lock::lock_holder<LIBCOPP_COPP_NAMESPACE_ID::util::lock::spin_lock> lock_guard{
1105  action_lock_};
1106 # endif
1107 
1108  using iter_type = typename container_type::iterator;
1109  iter_type iter = tasks_.find(id);
1110  if (tasks_.end() == iter) {
1112  }
1113  if (nullptr != confirm_context && iter->second.task_.get_context().get() != confirm_context) {
1115  }
1116 
1117  // make sure running task be killed first
1118  task_inst = std::move(iter->second.task_);
1119 
1120  remove_timeout_timer(iter->second);
1121  tasks_.erase(iter);
1122  }
1123 
1124  if (task_inst.get_context()) {
1125 # if defined(LIBCOTASK_MACRO_AUTO_CLEANUP_MANAGER) && LIBCOTASK_MACRO_AUTO_CLEANUP_MANAGER
1126  using task_manager_helper = typename task_type::task_manager_helper;
1127  // already cleanup, there is no need to cleanup again
1128  task_manager_helper::cleanup_task_manager(*task_inst.get_context(), reinterpret_cast<void *>(this));
1129 # endif
1130 
1131  task_status_type task_status = task_inst.get_status();
1132  if (task_status > task_status_type::kCreated && task_status < task_status_type::kDone) {
1133  return task_inst.kill(task_status_type::kKilled);
1134  }
1135  }
1136 
1138  }
1139 
1145  const task_type *find_task(id_type id) noexcept {
1146  if (flags_ & static_cast<uint32_t>(flag_type::kTimerReset)) {
1147  return nullptr;
1148  }
1149 
1150 # if !defined(LIBCOPP_DISABLE_ATOMIC_LOCK) || !(LIBCOPP_DISABLE_ATOMIC_LOCK)
1151  LIBCOPP_COPP_NAMESPACE_ID::util::lock::lock_holder<LIBCOPP_COPP_NAMESPACE_ID::util::lock::spin_lock> lock_guard{
1152  action_lock_};
1153 # endif
1154 
1155  auto iter = tasks_.find(id);
1156  if (tasks_.end() == iter) {
1157  return nullptr;
1158  }
1159 
1160  return &iter->second.task_;
1161  }
1162 
1163  int start(id_type id) {
1164  if (flags_ & static_cast<uint32_t>(flag_type::kTimerReset)) {
1166  }
1167 
1168  task_type task_inst;
1169  {
1170 # if !defined(LIBCOPP_DISABLE_ATOMIC_LOCK) || !(LIBCOPP_DISABLE_ATOMIC_LOCK)
1171  LIBCOPP_COPP_NAMESPACE_ID::util::lock::lock_holder<LIBCOPP_COPP_NAMESPACE_ID::util::lock::spin_lock> lock_guard{
1172  action_lock_};
1173 # endif
1174 
1175  using iter_type = typename container_type::iterator;
1176  iter_type iter = tasks_.find(id);
1177  if (tasks_.end() == iter) return LIBCOPP_COPP_NAMESPACE_ID::COPP_EC_NOT_FOUND;
1178 
1179  task_inst = iter->second.task_;
1180  }
1181 
1182  // unlock and then run start
1183  if (task_inst.get_context()) {
1184  task_inst.start();
1185 
1186  // if task is finished, remove it
1187  if (task_inst.is_exiting()) {
1188  remove_task(id, task_inst);
1189  }
1190 
1192  } else {
1194  }
1195  }
1196 
1197  int cancel(id_type id) {
1198  if (flags_ & static_cast<uint32_t>(flag_type::kTimerReset)) {
1200  }
1201 
1202  task_type task_inst;
1203  {
1204 # if !defined(LIBCOPP_DISABLE_ATOMIC_LOCK) || !(LIBCOPP_DISABLE_ATOMIC_LOCK)
1205  LIBCOPP_COPP_NAMESPACE_ID::util::lock::lock_holder<LIBCOPP_COPP_NAMESPACE_ID::util::lock::spin_lock> lock_guard{
1206  action_lock_};
1207 # endif
1208 
1209  using iter_type = typename container_type::iterator;
1210  iter_type iter = tasks_.find(id);
1211  if (tasks_.end() == iter) {
1213  }
1214 
1215  task_inst = std::move(iter->second.task_);
1216 
1217  remove_timeout_timer(iter->second);
1218  tasks_.erase(iter); // remove from container
1219  }
1220 
1221  // unlock and then run cancel
1222  if (task_inst.get_context()) {
1223 # if defined(LIBCOTASK_MACRO_AUTO_CLEANUP_MANAGER) && LIBCOTASK_MACRO_AUTO_CLEANUP_MANAGER
1224  using task_manager_helper = typename task_type::task_manager_helper;
1225  // already cleanup, there is no need to cleanup again
1226  task_manager_helper::cleanup_task_manager(*task_inst.get_context(), reinterpret_cast<void *>(this));
1227 # endif
1228  task_inst.cancel();
1230  } else {
1232  }
1233  }
1234 
1235  int kill(id_type id, task_status_type target_status) {
1236  if (flags_ & static_cast<uint32_t>(flag_type::kTimerReset)) {
1238  }
1239 
1240  task_type task_inst;
1241  {
1242 # if !defined(LIBCOPP_DISABLE_ATOMIC_LOCK) || !(LIBCOPP_DISABLE_ATOMIC_LOCK)
1243  LIBCOPP_COPP_NAMESPACE_ID::util::lock::lock_holder<LIBCOPP_COPP_NAMESPACE_ID::util::lock::spin_lock> lock_guard{
1244  action_lock_};
1245 # endif
1246 
1247  using iter_type = typename container_type::iterator;
1248  iter_type iter = tasks_.find(id);
1249  if (tasks_.end() == iter) {
1251  }
1252 
1253  task_inst = std::move(iter->second.task_);
1254 
1255  remove_timeout_timer(iter->second);
1256  tasks_.erase(iter); // remove from container
1257  }
1258 
1259  // unlock and then run kill
1260  if (task_inst.get_context()) {
1261 # if defined(LIBCOTASK_MACRO_AUTO_CLEANUP_MANAGER) && LIBCOTASK_MACRO_AUTO_CLEANUP_MANAGER
1262  using task_manager_helper = typename task_type::task_manager_helper;
1263  // already cleanup, there is no need to cleanup again
1264  task_manager_helper::cleanup_task_manager(*task_inst.get_context(), reinterpret_cast<void *>(this));
1265 # endif
1266  task_inst.kill(target_status);
1268  } else {
1270  }
1271  }
1272 
1273  int kill(id_type id) { return kill(id, task_status_type::kKilled); }
1274 
1283  int tick(time_t sec, int nsec = 0) {
1284  detail::tickspec_t now_tick_time;
1285  // time can not be back
1286  if (sec < last_tick_time_.tv_sec || (sec == last_tick_time_.tv_sec && nsec <= last_tick_time_.tv_nsec)) {
1287  return 0;
1288  }
1289 
1290  now_tick_time.tv_sec = sec;
1291  now_tick_time.tv_nsec = nsec;
1292 
1293  // we will ignore tick when in a recursive call
1294  flag_guard_type tick_flag(&flags_, flag_type::kTimerTick);
1295  if (!tick_flag) {
1297  }
1298 
1299  if (flags_ & static_cast<uint32_t>(flag_type::kTimerReset)) {
1301  }
1302 
1303  // first tick, init and reset task timeout
1304  if (0 == last_tick_time_.tv_sec && 0 == last_tick_time_.tv_nsec) {
1305  // hold lock
1306 # if !defined(LIBCOPP_DISABLE_ATOMIC_LOCK) || !(LIBCOPP_DISABLE_ATOMIC_LOCK)
1307  LIBCOPP_COPP_NAMESPACE_ID::util::lock::lock_holder<LIBCOPP_COPP_NAMESPACE_ID::util::lock::spin_lock> lock_guard{
1308  action_lock_};
1309 # endif
1310 
1311  std::set<detail::task_timer_node<id_type>> real_checkpoints;
1312  for (typename std::set<detail::task_timer_node<id_type>>::iterator iter = task_timeout_timer_.begin();
1313  task_timeout_timer_.end() != iter; ++iter) {
1314  detail::task_timer_node<id_type> new_checkpoint = (*iter);
1315  new_checkpoint.expired_time.tv_sec += sec;
1316  new_checkpoint.expired_time.tv_nsec += nsec;
1317  real_checkpoints.insert(new_checkpoint);
1318  }
1319 
1320  task_timeout_timer_.swap(real_checkpoints);
1321  for (typename std::set<detail::task_timer_node<id_type>>::iterator iter = task_timeout_timer_.begin();
1322  task_timeout_timer_.end() != iter; ++iter) {
1323  const typename std::set<detail::task_timer_node<id_type>>::value_type &checkpoint = *iter;
1324  using co_iter_type = typename container_type::iterator;
1325  co_iter_type co_iter = tasks_.find(checkpoint.task_id);
1326 
1327  if (tasks_.end() != co_iter) {
1328  co_iter->second.timer_node = iter;
1329  }
1330  }
1331  last_tick_time_ = now_tick_time;
1333  }
1334 
1335  // remove timeout tasks
1336  while (false == task_timeout_timer_.empty()) {
1337  task_type task_inst;
1338 
1339  {
1340  // hold lock
1341 # if !defined(LIBCOPP_DISABLE_ATOMIC_LOCK) || !(LIBCOPP_DISABLE_ATOMIC_LOCK)
1342  LIBCOPP_COPP_NAMESPACE_ID::util::lock::lock_holder<LIBCOPP_COPP_NAMESPACE_ID::util::lock::spin_lock> lock_guard{
1343  action_lock_};
1344 # endif
1345 
1346  const typename std::set<detail::task_timer_node<id_type>>::value_type &timer_node =
1347  *task_timeout_timer_.begin();
1348  // all tasks those expired time less than now are timeout
1349  if (now_tick_time <= timer_node.expired_time) {
1350  break;
1351  }
1352 
1353  // check expire time(may be changed)
1354  using iter_type = typename container_type::iterator;
1355 
1356  iter_type iter = tasks_.find(timer_node.task_id);
1357 
1358  if (tasks_.end() != iter) {
1359  // task may be removed before
1360  task_inst = std::move(iter->second.task_);
1361 
1362  remove_timeout_timer(iter->second);
1363  tasks_.erase(iter); // remove from container
1364  }
1365  }
1366 
1367  // task call can not be used when lock is on
1368  if (!task_inst.is_exiting()) {
1369 # if defined(LIBCOTASK_MACRO_AUTO_CLEANUP_MANAGER) && LIBCOTASK_MACRO_AUTO_CLEANUP_MANAGER
1370  using task_manager_helper = typename task_type::task_manager_helper;
1371  // already cleanup, there is no need to cleanup again
1372  task_manager_helper::cleanup_task_manager(*task_inst.get_context(), reinterpret_cast<void *>(this));
1373 # endif
1374  task_inst.kill(task_status_type::kTimeout);
1375  }
1376  }
1377 
1378  last_tick_time_ = now_tick_time;
1380  }
1381 
1386  size_t get_tick_checkpoint_size() const LIBCOPP_MACRO_NOEXCEPT { return task_timeout_timer_.size(); }
1387 
1392  size_t get_task_size() const LIBCOPP_MACRO_NOEXCEPT { return tasks_.size(); }
1393 
1398  detail::tickspec_t get_last_tick_time() const LIBCOPP_MACRO_NOEXCEPT { return last_tick_time_; }
1399 
1404  inline const container_type &get_container() const LIBCOPP_MACRO_NOEXCEPT { return tasks_; }
1405 
1410  inline const std::set<detail::task_timer_node<id_type>> &get_checkpoints() const LIBCOPP_MACRO_NOEXCEPT {
1411  return task_timeout_timer_;
1412  }
1413 
1414  private:
1415  void set_timeout_timer(detail::task_manager_node<task_type> &node, time_t timeout_sec, int timeout_nsec) {
1416  remove_timeout_timer(node);
1417 
1418  if (timeout_sec <= 0 && timeout_nsec <= 0) {
1419  return;
1420  }
1421 
1422  if (!node.task_.get_context()) {
1423  return;
1424  }
1425 
1427  timer_node.task_id = node.task_.get_id();
1428  timer_node.expired_time.tv_sec = last_tick_time_.tv_sec + timeout_sec;
1429  timer_node.expired_time.tv_nsec = last_tick_time_.tv_nsec + timeout_nsec;
1430 
1431  std::pair<typename std::set<detail::task_timer_node<id_type>>::iterator, bool> res =
1432  task_timeout_timer_.insert(timer_node);
1433  if (res.second) {
1434  node.timer_node = res.first;
1435  }
1436  }
1437 
1438  void remove_timeout_timer(detail::task_manager_node<task_type> &node) {
1439  if (node.timer_node != task_timeout_timer_.end()) {
1440  task_timeout_timer_.erase(node.timer_node);
1441  node.timer_node = task_timeout_timer_.end();
1442  }
1443  }
1444 
1445 # if defined(LIBCOTASK_MACRO_AUTO_CLEANUP_MANAGER) && LIBCOTASK_MACRO_AUTO_CLEANUP_MANAGER
1446  static void task_cleanup_callback(void *self_ptr, task_context_base<TVALUE> &task_inst) {
1447  if (nullptr == self_ptr) {
1448  return;
1449  }
1450 
1451  reinterpret_cast<self_type *>(self_ptr)->remove_task(task_inst.get_id(), &task_inst);
1452  }
1453 # endif
1454 
1455  private:
1456  container_type tasks_;
1457  detail::tickspec_t last_tick_time_;
1458  std::set<detail::task_timer_node<id_type>> task_timeout_timer_;
1459 
1460 # if !defined(LIBCOPP_DISABLE_ATOMIC_LOCK) || !(LIBCOPP_DISABLE_ATOMIC_LOCK)
1461  LIBCOPP_COPP_NAMESPACE_ID::util::lock::spin_lock action_lock_;
1462 # endif
1463  uint32_t flags_;
1464 };
1465 #endif
1466 
1467 LIBCOPP_COTASK_NAMESPACE_END
LIBCOPP_COTASK_API bool is_exiting() const LIBCOPP_MACRO_NOEXCEPT
check if a cotask is exiting
Definition: task_impl.cpp:47
UTIL_FORCEINLINE id_type get_id() const LIBCOPP_MACRO_NOEXCEPT
Definition: task_impl.h:74
int start(id_type id, void *priv_data=nullptr)
Definition: task_manager.h:472
void remove_timeout_timer(detail::task_manager_node< task_type > &node)
Definition: task_manager.h:841
int kill(id_type id, enum EN_TASK_STATUS status, void *priv_data=nullptr)
Definition: task_manager.h:623
int kill(id_type id, void *priv_data=nullptr)
Definition: task_manager.h:665
detail::tickspec_t get_last_tick_time() const LIBCOPP_MACRO_NOEXCEPT
get last tick time
Definition: task_manager.h:801
int remove_task(id_type id)
remove task in this manager
Definition: task_manager.h:385
typename task_type::id_type id_type
Definition: task_manager.h:163
static ptr_type create()
create a new task manager
Definition: task_manager.h:255
size_t get_task_size() const LIBCOPP_MACRO_NOEXCEPT
get task number in this manager
Definition: task_manager.h:795
task_manager< task_type > self_type
Definition: task_manager.h:165
int remove_task(id_type id, const task_type *confirm_ptr)
remove task in this manager
Definition: task_manager.h:393
int tick(time_t sec, int nsec=0)
active tick event and deal with clock
Definition: task_manager.h:675
int cancel(id_type id, void *priv_data=nullptr)
Definition: task_manager.h:570
int add_task(const task_ptr_type &task, time_t timeout_sec, int timeout_nsec)
add task to manager please make the task has method of get_id() and will return a unique id
Definition: task_manager.h:270
task_ptr_type find_task(id_type id)
find task by id
Definition: task_manager.h:442
LIBCOPP_COPP_NAMESPACE_ID::util::lock::spin_lock action_lock_
Definition: task_manager.h:864
std::shared_ptr< self_type > ptr_type
Definition: task_manager.h:166
detail::tickspec_t last_tick_time_
Definition: task_manager.h:860
typename task_type::ptr_type task_ptr_type
Definition: task_manager.h:164
int remove_task(id_type id, const task_ptr_type &confirm_ptr)
remove task in this manager
Definition: task_manager.h:378
std::set< detail::task_timer_node< id_type > > task_timeout_timer_
Definition: task_manager.h:861
const container_type & get_container() const LIBCOPP_MACRO_NOEXCEPT
task container, this api is just used for provide information to users
Definition: task_manager.h:807
size_t get_tick_checkpoint_size() const LIBCOPP_MACRO_NOEXCEPT
get timeout checkpoint number in this manager
Definition: task_manager.h:789
int resume(id_type id, void *priv_data=nullptr)
Definition: task_manager.h:521
const std::set< detail::task_timer_node< id_type > > & get_checkpoints() const LIBCOPP_MACRO_NOEXCEPT
get all task checkpoints, this api is just used for provide information to users
Definition: task_manager.h:813
std::unordered_map< typename task_type::id_type, detail::task_manager_node< task_type > > container_type
Definition: task_manager.h:162
void set_timeout_timer(detail::task_manager_node< task_type > &node, time_t timeout_sec, int timeout_nsec)
Definition: task_manager.h:818
int set_timeout(id_type id, time_t timeout_sec, int timeout_nsec)
set or update task timeout
Definition: task_manager.h:351
int add_task(const task_ptr_type &task)
add task to manager please make the task has method of get_id() and will return a unique id
Definition: task_manager.h:335
Definition: task.h:37
typename impl::task_impl::id_type id_type
Definition: task.h:46
LIBCOPP_COPP_NAMESPACE_ID::util::intrusive_ptr< self_type > ptr_type
Definition: task.h:41
@ COPP_EC_EXTERNAL_INSERT_FAILED
COPP_EC_EXTERNAL_INSERT_FAILED.
Definition: errno.h:15
@ COPP_EC_IN_RESET
COPP_EC_IN_RESET.
Definition: errno.h:17
@ COPP_EC_CAST_FAILED
COPP_EC_CAST_FAILED.
Definition: errno.h:31
@ COPP_EC_SUCCESS
COPP_EC_SUCCESS.
Definition: errno.h:12
@ COPP_EC_NOT_FOUND
COPP_EC_NOT_FOUND.
Definition: errno.h:28
@ COPP_EC_TASK_IS_EXITING
COPP_EC_TASK_IS_EXITING.
Definition: errno.h:38
@ COPP_EC_ALREADY_EXIST
COPP_EC_ALREADY_EXIST.
Definition: errno.h:29
@ COPP_EC_TASK_ALREADY_IN_ANOTHER_MANAGER
COPP_EC_TASK_ALREADY_IN_ANOTHER_MANAGER.
Definition: errno.h:41
@ COPP_EC_ARGS_ERROR
COPP_EC_ARGS_ERROR.
Definition: errno.h:30
struct LIBCOPP_COTASK_API_HEAD_ONLY task_manager_node
Definition: task_manager.h:130
std::shared_ptr< cli::cmd_option_value > value_type
Definition: cmd_option.h:50
my_task_t::ptr_t task_ptr_type
void tick()
std::set< task_timer_node< typename task< TCO_MACRO >::id_type > >::iterator timer_node
Definition: task_manager.h:137
typename task< TCO_MACRO >::ptr_type task_ptr_type
Definition: task_manager.h:134
TTASK_ID_TYPE task_id
Definition: task_manager.h:76
friend bool operator==(const task_timer_node &l, const task_timer_node &r)
Definition: task_manager.h:78
friend bool operator!=(const task_timer_node &l, const task_timer_node &r)
Definition: task_manager.h:91
friend bool operator>=(const task_timer_node &l, const task_timer_node &r)
Definition: task_manager.h:119
friend bool operator<(const task_timer_node &l, const task_timer_node &r)
Definition: task_manager.h:95
friend bool operator>(const task_timer_node &l, const task_timer_node &r)
Definition: task_manager.h:111
friend bool operator<=(const task_timer_node &l, const task_timer_node &r)
Definition: task_manager.h:103
friend bool operator!=(const tickspec_t &l, const tickspec_t &r)
Definition: task_manager.h:51
friend bool operator>=(const tickspec_t &l, const tickspec_t &r)
Definition: task_manager.h:67
friend bool operator==(const tickspec_t &l, const tickspec_t &r)
Definition: task_manager.h:42
friend bool operator<(const tickspec_t &l, const tickspec_t &r)
Definition: task_manager.h:55
friend bool operator>(const tickspec_t &l, const tickspec_t &r)
Definition: task_manager.h:63
friend bool operator<=(const tickspec_t &l, const tickspec_t &r)
Definition: task_manager.h:59
flag_guard_type(int *flags, typename flag_type::type v)
Definition: task_manager.h:189
EN_TASK_STATUS
Definition: task_impl.h:27
@ EN_TS_DONE
Definition: task_impl.h:32
@ EN_TS_CREATED
Definition: task_impl.h:29
@ EN_TS_TIMEOUT
Definition: task_impl.h:35
@ EN_TS_KILLED
Definition: task_impl.h:34
class LIBCOPP_COTASK_API_HEAD_ONLY task_manager
Definition: task_manager.h:153