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