libcopp 2.3.1
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
task.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
10#include <libcopp/utils/config/libcopp_build_features.h>
11
13#include <libcopp/utils/errno.h>
15#include <libcotask/this_task.h>
16
17// clang-format off
18#include <libcopp/utils/config/stl_include_prefix.h> // NOLINT(build/include_order)
19// clang-format on
20#if defined(LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR) && LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR
21# include <exception>
22#endif
23
24#if defined(LIBCOPP_MACRO_ENABLE_WIN_FIBER) && LIBCOPP_MACRO_ENABLE_WIN_FIBER
25# include <type_traits>
26#endif
27#include <stdint.h>
28#include <algorithm>
29#include <cstddef>
30#include <list>
31// clang-format off
32#include <libcopp/utils/config/stl_include_suffix.h> // NOLINT(build/include_order)
33// clang-format on
34
35LIBCOPP_COTASK_NAMESPACE_BEGIN
36
37template <class TCO_MACRO = macro_coroutine>
38class LIBCOPP_COTASK_API_HEAD_ONLY task : public impl::task_impl {
39 public:
40 using macro_coroutine_type = TCO_MACRO;
42 using ptr_type = LIBCOPP_COPP_NAMESPACE_ID::memory::intrusive_ptr<self_type>;
43
44 using coroutine_type = typename macro_coroutine_type::coroutine_type;
45 using stack_allocator_type = typename macro_coroutine_type::stack_allocator_type;
46
49
50 // Compability with libcopp-1.x
53 using ptr_t = ptr_type;
57 using id_t = id_type;
58
59 private:
61 // Compability with libcopp-1.x
63
64 struct task_group {
65 std::list<std::pair<ptr_type, void *>> member_list_;
66 };
67
68 public:
73 task(size_t stack_sz)
74 : stack_size_(stack_sz),
75 action_destroy_fn_(nullptr)
76#if defined(LIBCOTASK_MACRO_AUTO_CLEANUP_MANAGER) && LIBCOTASK_MACRO_AUTO_CLEANUP_MANAGER
77 ,
78 binding_manager_ptr_(nullptr),
79 binding_manager_fn_(nullptr)
80#endif
81 {
82 }
83
91 template <typename TAct, typename Ty>
92 static LIBCOPP_COTASK_API_HEAD_ONLY ptr_type create_with_delegate(Ty &&callable,
93 typename coroutine_type::allocator_type &alloc,
94 size_t stack_size = 0,
95 size_t private_buffer_size = 0) {
96 using a_t = TAct;
97
98 if (0 == stack_size) {
99 stack_size = LIBCOPP_COPP_NAMESPACE_ID::stack_traits::default_size();
100 }
101
102 size_t action_size = coroutine_type::align_address_size(sizeof(a_t));
103 size_t task_size = coroutine_type::align_address_size(sizeof(self_type));
104
105 if (stack_size <= sizeof(impl::task_impl *) + private_buffer_size + action_size + task_size) {
106 return ptr_type();
107 }
108
109 typename coroutine_type::ptr_type coroutine =
110 coroutine_type::create(typename coroutine_type::callback_t(), alloc, stack_size,
111 sizeof(impl::task_impl *) + private_buffer_size, action_size + task_size);
112 if (!coroutine) {
113 return ptr_type();
114 }
115
116 void *action_addr = sub_buffer_offset(coroutine.get(), action_size);
117 void *task_addr = sub_buffer_offset(action_addr, task_size);
118
119 // placement new task
120 ptr_type ret(new (task_addr) self_type(stack_size));
121 if (!ret) {
122 return ret;
123 }
124
125 *(reinterpret_cast<impl::task_impl **>(coroutine->get_private_buffer())) = ret.get();
126 ret->coroutine_obj_ = coroutine;
127 ret->coroutine_obj_->set_flags(impl::task_impl::ext_coroutine_flag_t::EN_ECFT_COTASK);
128
129 // placement new action
130 a_t *action = new (action_addr) a_t(std::forward<Ty>(callable));
131 if (nullptr == action) {
132 return ret;
133 }
134
135 // redirect runner
136 coroutine->set_runner([action](void *private_data) { return (*action)(private_data); });
137
138 ret->action_destroy_fn_ = get_placement_destroy(action);
139 ret->_set_action(action);
140
141 return ret;
142 }
143
151 template <typename Ty>
152 static inline ptr_type create(Ty &&functor, size_t stack_size = 0, size_t private_buffer_size = 0) {
153 typename coroutine_type::allocator_type alloc;
154 return create(std::forward<Ty>(functor), alloc, stack_size, private_buffer_size);
155 }
156
157 template <typename Ty>
158 static inline ptr_type create(Ty &&functor, typename coroutine_type::allocator_type &alloc, size_t stack_size = 0,
159 size_t private_buffer_size = 0) {
160 using decay_type = typename std::decay<Ty>::type;
161 using a_t = typename std::conditional<std::is_base_of<impl::task_action_impl, decay_type>::value, decay_type,
163
164 return create_with_delegate<a_t>(std::forward<Ty>(functor), alloc, stack_size, private_buffer_size);
165 }
166
173 template <typename Ty>
174 static inline ptr_type create(Ty (*func)(void *), typename coroutine_type::allocator_type &alloc,
175 size_t stack_size = 0, size_t private_buffer_size = 0) {
176 using a_t = task_action_function<Ty>;
177
178 return create_with_delegate<a_t>(func, alloc, stack_size, private_buffer_size);
179 }
180
181 template <typename Ty>
182 static inline ptr_type create(Ty (*func)(void *), size_t stack_size = 0, size_t private_buffer_size = 0) {
183 typename coroutine_type::allocator_type alloc;
184 return create(func, alloc, stack_size, private_buffer_size);
185 }
186
193 template <typename Ty, typename TInst>
194 static LIBCOPP_COTASK_API_HEAD_ONLY ptr_type create(Ty(TInst::*func), TInst *instance,
195 typename coroutine_type::allocator_type &alloc,
196 size_t stack_size = 0, size_t private_buffer_size = 0) {
198
199 return create<a_t>(a_t(func, instance), alloc, stack_size, private_buffer_size);
200 }
201
202 template <typename Ty, typename TInst>
203 static inline ptr_type create(Ty(TInst::*func), TInst *instance, size_t stack_size = 0,
204 size_t private_buffer_size = 0) {
205 typename coroutine_type::allocator_type alloc;
206 return create(func, instance, alloc, stack_size, private_buffer_size);
207 }
208
215 template <typename Ty, typename... TParams>
216 static LIBCOPP_COTASK_API_HEAD_ONLY ptr_type create_with(typename coroutine_type::allocator_type &alloc,
217 size_t stack_size, size_t private_buffer_size,
218 TParams &&...args) {
219 using a_t = Ty;
220
221 return create(a_t(std::forward<TParams>(args)...), alloc, stack_size, private_buffer_size);
222 }
223
232 inline ptr_type next(ptr_type next_task, void *priv_data = nullptr) {
233 // can not refers to self
234 if (this == next_task.get() || !next_task) {
235 return ptr_type(this);
236 }
237
238 // can not add next task when finished
239 if (is_exiting() || is_completed()) {
240 // run next task immedialy
241 EN_TASK_STATUS next_task_status = next_task->get_status();
242 if (EN_TS_CREATED == next_task_status) {
243 next_task->start(priv_data);
244 } else if (EN_TS_WAITING == next_task_status) {
245 next_task->resume(priv_data);
246 }
247 return next_task;
248 }
249
250#if LIBCOPP_MACRO_ENABLE_MULTI_THREAD
251 LIBCOPP_COPP_NAMESPACE_ID::util::lock::lock_holder<LIBCOPP_COPP_NAMESPACE_ID::util::lock::spin_lock> lock_guard(
252 inner_action_lock_);
253#endif
254
255 next_list_.member_list_.push_back(std::make_pair(next_task, priv_data));
256 return next_task;
257 }
258
267 template <typename Ty>
268 inline ptr_type next(Ty &&functor, void *priv_data = nullptr, size_t stack_size = 0, size_t private_buffer_size = 0) {
269 return next(create(std::forward<Ty>(functor), stack_size, private_buffer_size), priv_data);
270 }
271
272 template <typename Ty>
273 inline ptr_type next(Ty &&functor, typename coroutine_type::allocator_type &alloc, void *priv_data = nullptr,
274 size_t stack_size = 0, size_t private_buffer_size = 0) {
275 return next(create(std::forward<Ty>(functor), alloc, stack_size, private_buffer_size), priv_data);
276 }
277
286 template <typename Ty>
287 inline ptr_type next(Ty (*func)(void *), void *priv_data = nullptr, size_t stack_size = 0,
288 size_t private_buffer_size = 0) {
289 return next(create(func, stack_size, private_buffer_size), priv_data);
290 }
291
292 template <typename Ty>
293 inline ptr_type next(Ty (*func)(void *), typename coroutine_type::allocator_type &alloc, void *priv_data = nullptr,
294 size_t stack_size = 0, size_t private_buffer_size = 0) {
295 return next(create(func, alloc, stack_size, private_buffer_size), priv_data);
296 }
297
307 template <typename Ty, typename TInst>
308 inline ptr_type next(Ty(TInst::*func), TInst *instance, void *priv_data = nullptr, size_t stack_size = 0,
309 size_t private_buffer_size = 0) {
310 return next(create(func, instance, stack_size, private_buffer_size), priv_data);
311 }
312
313 template <typename Ty, typename TInst>
314 inline ptr_type next(Ty(TInst::*func), TInst *instance, typename coroutine_type::allocator_type &alloc,
315 void *priv_data = nullptr, size_t stack_size = 0, size_t private_buffer_size = 0) {
316 return next(create(func, instance, alloc, stack_size, private_buffer_size), priv_data);
317 }
318
327 inline int await_task(ptr_type wait_task) {
328 if (!wait_task) {
329 return LIBCOPP_COPP_NAMESPACE_ID::COPP_EC_ARGS_ERROR;
330 }
331
332 if (this == wait_task.get()) {
333 return LIBCOPP_COPP_NAMESPACE_ID::COPP_EC_TASK_CAN_NOT_WAIT_SELF;
334 }
335
336 // if target is exiting or completed, just return
337 if (wait_task->is_exiting() || wait_task->is_completed()) {
338 return LIBCOPP_COPP_NAMESPACE_ID::COPP_EC_TASK_IS_EXITING;
339 }
340
341 if (is_exiting()) {
342 return LIBCOPP_COPP_NAMESPACE_ID::COPP_EC_TASK_IS_EXITING;
343 }
344
345 if (this_task() != this) {
346 return LIBCOPP_COPP_NAMESPACE_ID::COPP_EC_TASK_NOT_IN_ACTION;
347 }
348
349 // add to next list failed
350 if (wait_task->next(ptr_type(this)).get() != this) {
351 return LIBCOPP_COPP_NAMESPACE_ID::COPP_EC_TASK_ADD_NEXT_FAILED;
352 }
353
354 int ret = 0;
355 while (!(wait_task->is_exiting() || wait_task->is_completed())) {
356 if (is_exiting()) {
357 return LIBCOPP_COPP_NAMESPACE_ID::COPP_EC_TASK_IS_EXITING;
358 }
359
360 ret = yield();
361 }
362
363 return ret;
364 }
365
374 template <class TAWAITABLE, class TERROR_TRANSFORM,
375 class = LIBCOPP_COPP_NAMESPACE_ID::nostd::enable_if_t<LIBCOPP_COPP_NAMESPACE_ID::stackful_inject_awaitable<
376 LIBCOPP_COPP_NAMESPACE_ID::nostd::remove_cvref_t<TAWAITABLE>>::value>>
377 inline LIBCOPP_COPP_NAMESPACE_ID::container_value_type<TAWAITABLE>
378 await_value(TAWAITABLE &&awaitable, TERROR_TRANSFORM &&error_transform) noexcept(
379 std::is_nothrow_copy_constructible<LIBCOPP_COPP_NAMESPACE_ID::container_value_type<TAWAITABLE>>::value &&
380 noexcept(error_transform(LIBCOPP_COPP_NAMESPACE_ID::COPP_EC_ARGS_ERROR))) {
381 if (!coroutine_obj_) {
382 return error_transform(LIBCOPP_COPP_NAMESPACE_ID::COPP_EC_NOT_INITED);
383 }
384
385 return awaitable.inject_await(this, std::forward<TERROR_TRANSFORM>(error_transform));
386 }
387
396 template <class TAWAITABLE,
397 class = LIBCOPP_COPP_NAMESPACE_ID::nostd::enable_if_t<LIBCOPP_COPP_NAMESPACE_ID::stackful_inject_awaitable<
398 LIBCOPP_COPP_NAMESPACE_ID::nostd::remove_cvref_t<TAWAITABLE>>::value>>
399 inline LIBCOPP_COPP_NAMESPACE_ID::container_value_type<TAWAITABLE> await_value(TAWAITABLE &&awaitable) noexcept(
400 std::is_nothrow_copy_constructible<LIBCOPP_COPP_NAMESPACE_ID::container_value_type<TAWAITABLE>>::value &&
401 noexcept(LIBCOPP_COPP_NAMESPACE_ID::stackful_channel_error_transform<
402 LIBCOPP_COPP_NAMESPACE_ID::container_value_type<TAWAITABLE>>()(
403 LIBCOPP_COPP_NAMESPACE_ID::COPP_EC_ARGS_ERROR))) {
404 if (!coroutine_obj_) {
405 return LIBCOPP_COPP_NAMESPACE_ID::stackful_channel_error_transform<
406 LIBCOPP_COPP_NAMESPACE_ID::container_value_type<TAWAITABLE>>()(LIBCOPP_COPP_NAMESPACE_ID::COPP_EC_NOT_INITED);
407 }
408
409 return awaitable.inject_await(this, LIBCOPP_COPP_NAMESPACE_ID::stackful_channel_error_transform<
410 LIBCOPP_COPP_NAMESPACE_ID::container_value_type<TAWAITABLE>>());
411 }
412
417 template <typename TTask>
418 inline int await_task(TTask *wait_task) {
419 return await_task(ptr_type(wait_task));
420 }
421
430 inline ptr_type then(ptr_type next_task, void *priv_data = nullptr) { return next(next_task, priv_data); }
431
439 template <typename Ty>
440 inline ptr_type then(Ty &&functor, void *priv_data = nullptr) {
441 if (!coroutine_obj_) {
442 then(create(std::forward<Ty>(functor), stack_size_, get_private_buffer_size()), priv_data);
443 }
444
445 return then(
446 create(std::forward<Ty>(functor), coroutine_obj_->get_allocator(), stack_size_, get_private_buffer_size()),
447 priv_data);
448 }
449
450 template <typename Ty>
451 inline ptr_type then(Ty (*func)(void *), void *priv_data = nullptr) {
452 if (!coroutine_obj_) {
453 return then(create(func, stack_size_, get_private_buffer_size()), priv_data);
454 }
455
456 return then(create(func, coroutine_obj_->get_allocator(), stack_size_, get_private_buffer_size()), priv_data);
457 }
458
464#if defined(LIBCOPP_MACRO_ENABLE_RTTI) && LIBCOPP_MACRO_ENABLE_RTTI
465 return dynamic_cast<self_type *>(impl::task_impl::this_task());
466#else
467 return static_cast<self_type *>(impl::task_impl::this_task());
468#endif
469 }
470
471 public:
472 virtual ~task() {
473 EN_TASK_STATUS status = get_status();
474 // inited but not finished will trigger timeout or finish other actor
475 if (status < EN_TS_DONE && status > EN_TS_CREATED) {
477 } else if (status <= EN_TS_CREATED) {
478#if defined(LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR) && LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR
479 std::list<std::exception_ptr> eptrs;
480 active_next_tasks(eptrs);
481 // next tasks
482 maybe_rethrow(eptrs);
483#else
484 active_next_tasks();
485#endif
486 }
487
488 // then, find and destroy action
489 void *action_ptr = reinterpret_cast<void *>(_get_action());
490 if (nullptr != action_destroy_fn_ && nullptr != action_ptr) {
491 (*action_destroy_fn_)(action_ptr);
492
493 action_destroy_fn_ = nullptr;
494 _set_action(nullptr);
495 }
496 }
497
498 inline typename coroutine_type::ptr_type &get_coroutine_context() LIBCOPP_MACRO_NOEXCEPT { return coroutine_obj_; }
499 inline const typename coroutine_type::ptr_type &get_coroutine_context() const LIBCOPP_MACRO_NOEXCEPT {
500 return coroutine_obj_;
501 }
502
503 public:
504 int get_ret_code() const override {
505 if (!coroutine_obj_) {
506 return 0;
507 }
508
509 return coroutine_obj_->get_ret_code();
510 }
511
512#if defined(LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR) && LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR
513 int start(void *priv_data, EN_TASK_STATUS expected_status = EN_TS_CREATED) override {
514 std::list<std::exception_ptr> eptrs;
515 int ret = start(eptrs, priv_data, expected_status);
516 maybe_rethrow(eptrs);
517 return ret;
518 }
519
520 virtual int start(std::list<std::exception_ptr> &unhandled, void *priv_data,
521 EN_TASK_STATUS expected_status = EN_TS_CREATED) LIBCOPP_MACRO_NOEXCEPT {
522#else
523 int start(void *priv_data, EN_TASK_STATUS expected_status = EN_TS_CREATED) override {
524#endif
525 if (!coroutine_obj_) {
526 return LIBCOPP_COPP_NAMESPACE_ID::COPP_EC_NOT_INITED;
527 }
528
529 EN_TASK_STATUS from_status = expected_status;
530
531 do {
532 if LIBCOPP_UTIL_UNLIKELY_CONDITION (from_status >= EN_TS_DONE) {
533 return LIBCOPP_COPP_NAMESPACE_ID::COPP_EC_ALREADY_FINISHED;
534 }
535
537 return LIBCOPP_COPP_NAMESPACE_ID::COPP_EC_IS_RUNNING;
538 }
539
540 if LIBCOPP_UTIL_LIKELY_CONDITION (_cas_status(from_status, EN_TS_RUNNING)) { // Atomic.CAS here
541 break;
542 }
543 } while (true);
544
545 // use this smart ptr to avoid destroy of this
546 // ptr_type protect_from_destroy(this);
547
548#if defined(LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR) && LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR
549 std::exception_ptr eptr;
550 int ret = coroutine_obj_->start(eptr, priv_data);
551 if (eptr) {
552 unhandled.emplace_back(std::move(eptr));
553 }
554#else
555 int ret = coroutine_obj_->start(priv_data);
556#endif
557
558 from_status = EN_TS_RUNNING;
559 if (is_completed()) { // Atomic.CAS here
560 while (from_status < EN_TS_DONE) {
561 if LIBCOPP_UTIL_LIKELY_CONDITION (_cas_status(from_status, EN_TS_DONE)) { // Atomic.CAS here
562 break;
563 }
564 }
565
566 finish_priv_data_ = priv_data;
567#if defined(LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR) && LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR
568 _notify_finished(unhandled, priv_data);
569#else
570 _notify_finished(priv_data);
571#endif
572 return ret;
573 }
574
575 while (true) {
576 if (from_status >= EN_TS_DONE) { // canceled or killed
577#if defined(LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR) && LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR
578 _notify_finished(unhandled, finish_priv_data_);
579#else
580 _notify_finished(finish_priv_data_);
581#endif
582 break;
583 }
584
585 if LIBCOPP_UTIL_LIKELY_CONDITION (_cas_status(from_status, EN_TS_WAITING)) { // Atomic.CAS here
586 break;
587 // waiting
588 }
589 }
590
591 return ret;
592 }
593
594#if defined(LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR) && LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR
595 int resume(void *priv_data, EN_TASK_STATUS expected_status = EN_TS_WAITING) override {
596 return start(priv_data, expected_status);
597 }
598
599 virtual int resume(std::list<std::exception_ptr> &unhandled, void *priv_data,
600 EN_TASK_STATUS expected_status = EN_TS_WAITING) LIBCOPP_MACRO_NOEXCEPT {
601 return start(unhandled, priv_data, expected_status);
602 }
603#else
604 int resume(void *priv_data, EN_TASK_STATUS expected_status = EN_TS_WAITING) override {
605 return start(priv_data, expected_status);
606 }
607#endif
608
609 int yield(void **priv_data) override {
610 if (!coroutine_obj_) {
611 return LIBCOPP_COPP_NAMESPACE_ID::COPP_EC_NOT_INITED;
612 }
613
614 return coroutine_obj_->yield(priv_data);
615 }
616
617#if defined(LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR) && LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR
618 int cancel(void *priv_data) override {
619 std::list<std::exception_ptr> eptrs;
620 int ret = cancel(eptrs, priv_data);
621 maybe_rethrow(eptrs);
622 return ret;
623 }
624
625 virtual int cancel(std::list<std::exception_ptr> &unhandled, void *priv_data) LIBCOPP_MACRO_NOEXCEPT {
626#else
627 int cancel(void *priv_data) override {
628#endif
629
630 EN_TASK_STATUS from_status = get_status();
631
632 do {
633 if (EN_TS_RUNNING == from_status) {
634 return LIBCOPP_COPP_NAMESPACE_ID::COPP_EC_IS_RUNNING;
635 }
636
638 break;
639 }
640 } while (true);
641
642#if defined(LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR) && LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR
643 _notify_finished(unhandled, priv_data);
644#else
645 _notify_finished(priv_data);
646#endif
647 return LIBCOPP_COPP_NAMESPACE_ID::COPP_EC_SUCCESS;
648 }
649
650#if defined(LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR) && LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR
651 int kill(enum EN_TASK_STATUS status, void *priv_data) override {
652 std::list<std::exception_ptr> eptrs;
653 int ret = kill(eptrs, status, priv_data);
654 maybe_rethrow(eptrs);
655 return ret;
656 }
657
658 virtual int kill(std::list<std::exception_ptr> &unhandled, enum EN_TASK_STATUS status,
659 void *priv_data) LIBCOPP_MACRO_NOEXCEPT {
660#else
661 int kill(enum EN_TASK_STATUS status, void *priv_data) override {
662#endif
663 EN_TASK_STATUS from_status = get_status();
664
665 do {
666 if LIBCOPP_UTIL_LIKELY_CONDITION (_cas_status(from_status, status)) {
667 break;
668 }
669 } while (true);
670
671 if (EN_TS_RUNNING != from_status) {
672#if defined(LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR) && LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR
673 _notify_finished(unhandled, priv_data);
674#else
675 _notify_finished(priv_data);
676#endif
677 } else {
678 finish_priv_data_ = priv_data;
679 }
680
681 return LIBCOPP_COPP_NAMESPACE_ID::COPP_EC_SUCCESS;
682 }
683
689
690 public:
691 bool is_completed() const LIBCOPP_MACRO_NOEXCEPT override {
692 if (!coroutine_obj_) {
693 return false;
694 }
695
696 return coroutine_obj_->is_finished();
697 }
698
699#if defined(LIBCOPP_MACRO_ENABLE_WIN_FIBER) && LIBCOPP_MACRO_ENABLE_WIN_FIBER
700 bool is_fiber() const LIBCOPP_MACRO_NOEXCEPT override {
701 return std::is_base_of<LIBCOPP_COPP_NAMESPACE_ID::coroutine_context_fiber, coroutine_type>::value;
702 }
703#endif
704
705 static inline void *add_buffer_offset(void *in, size_t off) {
706 return reinterpret_cast<void *>(reinterpret_cast<unsigned char *>(in) + off);
707 }
708
709 static inline void *sub_buffer_offset(void *in, size_t off) {
710 return reinterpret_cast<void *>(reinterpret_cast<unsigned char *>(in) - off);
711 }
712
714 if (!coroutine_obj_) {
715 return nullptr;
716 }
717
718 return add_buffer_offset(coroutine_obj_->get_private_buffer(), sizeof(impl::task_impl *));
719 }
720
722 if (!coroutine_obj_) {
723 return 0;
724 }
725
726 return coroutine_obj_->get_private_buffer_size() - sizeof(impl::task_impl *);
727 }
728
729 inline size_t use_count() const { return ref_count_.load(); }
730
731#if defined(LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR) && LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR
732 LIBCOPP_UTIL_FORCEINLINE static void maybe_rethrow(std::list<std::exception_ptr> &eptrs) {
733 for (std::list<std::exception_ptr>::iterator iter = eptrs.begin(); iter != eptrs.end(); ++iter) {
734 coroutine_type::maybe_rethrow(*iter);
735 }
736 }
737#endif
738 private:
739 task(const task &) = delete;
740
741#if defined(LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR) && LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR
742 void active_next_tasks(std::list<std::exception_ptr> &unhandled) LIBCOPP_MACRO_NOEXCEPT {
743#else
745#endif
746 std::list<std::pair<ptr_type, void *>> next_list;
747#if defined(LIBCOTASK_MACRO_AUTO_CLEANUP_MANAGER) && LIBCOTASK_MACRO_AUTO_CLEANUP_MANAGER
748 void *manager_ptr;
749 void (*manager_fn)(void *, self_type &);
750#endif
751 // first, lock and swap container
752 {
753#if LIBCOPP_MACRO_ENABLE_MULTI_THREAD
754 LIBCOPP_COPP_NAMESPACE_ID::util::lock::lock_holder<LIBCOPP_COPP_NAMESPACE_ID::util::lock::spin_lock> lock_guard(
755 inner_action_lock_);
756#endif
757 next_list.swap(next_list_.member_list_);
758#if defined(LIBCOTASK_MACRO_AUTO_CLEANUP_MANAGER) && LIBCOTASK_MACRO_AUTO_CLEANUP_MANAGER
759 manager_ptr = binding_manager_ptr_;
760 manager_fn = binding_manager_fn_;
761 binding_manager_ptr_ = nullptr;
762 binding_manager_fn_ = nullptr;
763#endif
764 }
765
766 // then, do all the pending tasks
767 for (typename std::list<std::pair<ptr_type, void *>>::iterator iter = next_list.begin(); iter != next_list.end();
768 ++iter) {
769 if (!iter->first || EN_TS_INVALID == iter->first->get_status()) {
770 continue;
771 }
772
773#if defined(LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR) && LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR
774 if (iter->first->get_status() < EN_TS_RUNNING) {
775 iter->first->start(unhandled, iter->second);
776 } else {
777 iter->first->resume(unhandled, iter->second);
778 }
779#else
780 if (iter->first->get_status() < EN_TS_RUNNING) {
781 iter->first->start(iter->second);
782 } else {
783 iter->first->resume(iter->second);
784 }
785#endif
786 }
787
788#if defined(LIBCOPP_MACRO_ENABLE_STD_COROUTINE) && LIBCOPP_MACRO_ENABLE_STD_COROUTINE
789 caller_manager_.resume_callers();
790#endif
791
792 // finally, notify manager to cleanup(maybe start or resume with task's API but not task_manager's)
793#if defined(LIBCOTASK_MACRO_AUTO_CLEANUP_MANAGER) && LIBCOTASK_MACRO_AUTO_CLEANUP_MANAGER
794 if (nullptr != manager_ptr && nullptr != manager_fn) {
795 (*manager_fn)(manager_ptr, *this);
796 }
797#endif
798 }
799
801#if defined(LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR) && LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR
802 std::list<std::exception_ptr> &unhandled,
803#endif
804 void *priv_data) LIBCOPP_MACRO_NOEXCEPT {
805 // first, make sure coroutine finished.
806 if (coroutine_obj_ && false == coroutine_obj_->is_finished()) {
807 // make sure this task will not be destroyed when running
808 while (false == coroutine_obj_->is_finished()) {
809#if defined(LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR) && LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR
810 std::exception_ptr eptr;
811 coroutine_obj_->resume(eptr, priv_data);
812 if (eptr) {
813 unhandled.emplace_back(std::move(eptr));
814 }
815#else
816 coroutine_obj_->resume(priv_data);
817#endif
818 }
819 }
820
821#if defined(LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR) && LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR
822 int ret = impl::task_impl::_notify_finished(unhandled, priv_data);
823 // next tasks
824 active_next_tasks(unhandled);
825#else
826 int ret = impl::task_impl::_notify_finished(priv_data);
827 // next tasks
828 active_next_tasks();
829#endif
830 return ret;
831 }
832
834 if (p == nullptr) {
835 return;
836 }
837
838 ++p->ref_count_;
839 }
840
842 if (p == nullptr) {
843 return;
844 }
845
846 size_t left = --p->ref_count_;
847 if (0 == left) {
848 // save coroutine context first, make sure it's still available after destroy task
849 using this_coroutine_type = typename task<TCO_MACRO>::coroutine_type;
850 using this_coroutine_ptr_type = typename this_coroutine_type::ptr_type;
851 this_coroutine_ptr_type coro = p->coroutine_obj_;
852
853 // then, destruct task
854 p->~task();
855
856 // at last, destroy the coroutine and maybe recycle the stack space
857 coro.reset();
858 }
859 }
860
861#if defined(LIBCOTASK_MACRO_AUTO_CLEANUP_MANAGER) && LIBCOTASK_MACRO_AUTO_CLEANUP_MANAGER
862 public:
863 class LIBCOPP_COTASK_API_HEAD_ONLY task_manager_helper {
864 private:
865 template <class>
866 friend class LIBCOPP_COTASK_API_HEAD_ONLY task_manager;
867 static bool setup_task_manager(self_type &task_inst, void *manager_ptr, void (*fn)(void *, self_type &)) {
868# if LIBCOPP_MACRO_ENABLE_MULTI_THREAD
869 LIBCOPP_COPP_NAMESPACE_ID::util::lock::lock_holder<LIBCOPP_COPP_NAMESPACE_ID::util::lock::spin_lock> lock_guard(
870 task_inst.inner_action_lock_);
871# endif
872 if (task_inst.binding_manager_ptr_ != nullptr) {
873 return false;
874 }
875
876 task_inst.binding_manager_ptr_ = manager_ptr;
877 task_inst.binding_manager_fn_ = fn;
878 return true;
879 }
880
881 static bool cleanup_task_manager(self_type &task_inst, void *manager_ptr) {
882# if LIBCOPP_MACRO_ENABLE_MULTI_THREAD
883 LIBCOPP_COPP_NAMESPACE_ID::util::lock::lock_holder<LIBCOPP_COPP_NAMESPACE_ID::util::lock::spin_lock> lock_guard(
884 task_inst.inner_action_lock_);
885# endif
886 if (task_inst.binding_manager_ptr_ != manager_ptr) {
887 return false;
888 }
889
890 task_inst.binding_manager_ptr_ = nullptr;
891 task_inst.binding_manager_fn_ = nullptr;
892 return true;
893 }
894 };
895#endif
896
897#if defined(LIBCOPP_MACRO_ENABLE_STD_COROUTINE) && LIBCOPP_MACRO_ENABLE_STD_COROUTINE
898 public:
899 class LIBCOPP_COPP_API_HEAD_ONLY stackful_task_awaitable : public LIBCOPP_COPP_NAMESPACE_ID::awaitable_base_type {
900 public:
901 using value_type = typename macro_coroutine_type::value_type;
902
903 public:
904 explicit stackful_task_awaitable(self_type *waiting_task) : waiting_task_(waiting_task) {}
905
906 inline bool await_ready() const noexcept {
907 if (!waiting_task_) {
908 return true;
909 }
910
911 return waiting_task_->is_exiting();
912 }
913
914# if defined(LIBCOPP_MACRO_ENABLE_CONCEPTS) && LIBCOPP_MACRO_ENABLE_CONCEPTS
915 template <LIBCOPP_COPP_NAMESPACE_ID::DerivedPromiseBaseType TCPROMISE>
916# else
917 template <class TCPROMISE, typename = std::enable_if_t<
918 std::is_base_of<LIBCOPP_COPP_NAMESPACE_ID::promise_base_type, TCPROMISE>::value>>
919# endif
920 inline void await_suspend(LIBCOPP_MACRO_STD_COROUTINE_NAMESPACE coroutine_handle<TCPROMISE> caller) noexcept {
921 if (waiting_task_ && !waiting_task_->is_exiting() &&
922 caller.promise().get_status() < LIBCOPP_COPP_NAMESPACE_ID::promise_status::kDone) {
923 set_caller(caller);
924 waiting_task_->caller_manager_.add_caller(
925 LIBCOPP_COPP_NAMESPACE_ID::promise_caller_manager::handle_delegate{caller});
926
927 // Allow kill resume to forward error information
928 caller.promise().set_flag(LIBCOPP_COPP_NAMESPACE_ID::promise_flag::kInternalWaitting, true);
929 } else {
930 // Already done and can not suspend again
931 caller.resume();
932 }
933 }
934
935 inline value_type await_resume() {
936 if (!waiting_task_) {
937 return LIBCOPP_COPP_NAMESPACE_ID::COPP_EC_NOT_INITED;
938 }
939
940 value_type ret;
941 if (waiting_task_->is_exiting()) {
942 switch (waiting_task_->get_status()) {
943 case EN_TS_CANCELED:
944 case EN_TS_KILLED:
945 case EN_TS_TIMEOUT:
946 ret = LIBCOPP_COPP_NAMESPACE_ID::COPP_EC_TASK_IS_EXITING;
947 break;
948 default:
949 ret = waiting_task_->get_ret_code();
950 break;
951 }
952 } else {
953 ret = LIBCOPP_COPP_NAMESPACE_ID::COPP_EC_TASK_IS_KILLED;
954 }
955
956 // caller maybe null if the callable is already ready when co_await
957 auto caller = get_caller();
958
959 if (caller) {
960 if (nullptr != caller.promise) {
961 caller.promise->set_flag(LIBCOPP_COPP_NAMESPACE_ID::promise_flag::kInternalWaitting, false);
962 }
963
964 waiting_task_->caller_manager_.remove_caller(caller);
965 set_caller(nullptr);
966 }
967
968 return ret;
969 }
970
971 private:
972 // caller manager
973 self_type *waiting_task_;
974 };
975
976 auto operator co_await() & LIBCOPP_MACRO_NOEXCEPT { return stackful_task_awaitable{this}; }
977#endif
978 private:
980 typename coroutine_type::ptr_type coroutine_obj_;
982
983 // ============== action information ==============
984 void (*action_destroy_fn_)(void *);
985
986#if LIBCOPP_MACRO_ENABLE_MULTI_THREAD
987 LIBCOPP_COPP_NAMESPACE_ID::util::lock::atomic_int_type<size_t> ref_count_;
988 LIBCOPP_COPP_NAMESPACE_ID::util::lock::spin_lock inner_action_lock_;
989#else
990 LIBCOPP_COPP_NAMESPACE_ID::util::lock::atomic_int_type<LIBCOPP_COPP_NAMESPACE_ID::util::lock::unsafe_int_type<size_t>>
992#endif
993
994 // ============== binding to task manager ==============
995#if defined(LIBCOTASK_MACRO_AUTO_CLEANUP_MANAGER) && LIBCOTASK_MACRO_AUTO_CLEANUP_MANAGER
996 void *binding_manager_ptr_;
997 void (*binding_manager_fn_)(void *, self_type &);
998#endif
999
1000#if defined(LIBCOPP_MACRO_ENABLE_STD_COROUTINE) && LIBCOPP_MACRO_ENABLE_STD_COROUTINE
1001 LIBCOPP_COPP_NAMESPACE_ID::promise_caller_manager caller_manager_;
1002#endif
1003};
1004
1005#if defined(LIBCOPP_MACRO_ENABLE_STD_COROUTINE) && LIBCOPP_MACRO_ENABLE_STD_COROUTINE
1006template <typename TCO_MACRO>
1007auto operator co_await(const LIBCOPP_COPP_NAMESPACE_ID::memory::intrusive_ptr<task<TCO_MACRO>> &t)
1008 LIBCOPP_MACRO_NOEXCEPT {
1009 using awaitable = typename task<TCO_MACRO>::stackful_task_awaitable;
1010 return awaitable{t.get()};
1011}
1012#endif
1013LIBCOPP_COTASK_NAMESPACE_END
1014
1015LIBCOPP_COPP_NAMESPACE_BEGIN
1016template <class TCO_MACRO>
1017struct stackful_channel_resume_handle<LIBCOPP_COTASK_NAMESPACE_ID::task<TCO_MACRO>> {
1018 LIBCOPP_COPP_API_HEAD_ONLY inline static int resume(void *invoke_task, stackful_channel_context_base *priv_data) {
1019 if (nullptr != invoke_task) {
1020 return reinterpret_cast<LIBCOPP_COTASK_NAMESPACE_ID::task<TCO_MACRO> *>(invoke_task)
1021 ->resume(reinterpret_cast<void *>(priv_data));
1022 }
1023
1024 return 0;
1025 }
1026};
1027LIBCOPP_COPP_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 int cancel()
Definition task_impl.h:112
LIBCOPP_UTIL_FORCEINLINE EN_TASK_STATUS get_status() const LIBCOPP_MACRO_NOEXCEPT
Definition task_impl.h:82
LIBCOPP_COPP_NAMESPACE_ID::util::uint64_id_allocator id_allocator_type
Definition task_impl.h:45
LIBCOPP_COTASK_API void _set_action(action_ptr_type action)
Definition task_impl.cpp:65
task_action_impl * action_ptr_type
Definition task_impl.h:52
LIBCOPP_UTIL_FORCEINLINE int yield()
Definition task_impl.h:111
LIBCOPP_COPP_NAMESPACE_ID::util::uint64_id_allocator::value_type id_type
Definition task_impl.h:44
LIBCOPP_UTIL_FORCEINLINE int start()
Definition task_impl.h:109
LIBCOPP_UTIL_FORCEINLINE int resume()
Definition task_impl.h:110
LIBCOPP_COTASK_API action_ptr_type _get_action()
Definition task_impl.cpp:67
LIBCOPP_UTIL_FORCEINLINE int kill()
Definition task_impl.h:114
virtual LIBCOPP_COTASK_API bool is_completed() const LIBCOPP_MACRO_NOEXCEPT
Definition task_impl.cpp:41
LIBCOPP_COTASK_API bool _cas_status(EN_TASK_STATUS &expected, EN_TASK_STATUS desired)
Definition task_impl.cpp:69
static LIBCOPP_COTASK_API task_impl * this_task()
Definition task_impl.cpp:51
LIBCOPP_COTASK_API int _notify_finished(void *priv_data)
Definition task_impl.cpp:81
Definition task.h:38
ptr_type next(Ty &&functor, typename coroutine_type::allocator_type &alloc, void *priv_data=nullptr, size_t stack_size=0, size_t private_buffer_size=0)
Definition task.h:273
size_t get_private_buffer_size()
Definition task.h:721
ptr_type next(Ty(TInst::*func), TInst *instance, void *priv_data=nullptr, size_t stack_size=0, size_t private_buffer_size=0)
create next task with function
Definition task.h:308
static ptr_type create(Ty &&functor, size_t stack_size=0, size_t private_buffer_size=0)
create task with functor
Definition task.h:152
typename impl::task_impl::id_type id_type
Definition task.h:47
int yield(void **priv_data) override
Definition task.h:609
int _notify_finished(void *priv_data) LIBCOPP_MACRO_NOEXCEPT
Definition task.h:800
stack_allocator_type stack_allocator_t
Definition task.h:55
static ptr_type create(Ty(*func)(void *), size_t stack_size=0, size_t private_buffer_size=0)
Definition task.h:182
ptr_type next(ptr_type next_task, void *priv_data=nullptr)
add next task to run when task finished
Definition task.h:232
const coroutine_type::ptr_type & get_coroutine_context() const LIBCOPP_MACRO_NOEXCEPT
Definition task.h:499
static ptr_type create(Ty &&functor, typename coroutine_type::allocator_type &alloc, size_t stack_size=0, size_t private_buffer_size=0)
Definition task.h:158
task(size_t stack_sz)
constuctor
Definition task.h:73
int get_ret_code() const override
Definition task.h:504
void active_next_tasks()
Definition task.h:744
void * get_private_buffer()
Definition task.h:713
macro_coroutine_type macro_coroutine_t
Definition task.h:51
friend void intrusive_ptr_release(self_type *p)
Definition task.h:841
int await_task(TTask *wait_task)
await another task
Definition task.h:418
ptr_type then(Ty(*func)(void *), void *priv_data=nullptr)
Definition task.h:451
ptr_type next(Ty(*func)(void *), void *priv_data=nullptr, size_t stack_size=0, size_t private_buffer_size=0)
create next task with function
Definition task.h:287
static LIBCOPP_COTASK_API_HEAD_ONLY ptr_type create_with(typename coroutine_type::allocator_type &alloc, size_t stack_size, size_t private_buffer_size, TParams &&...args)
create task with functor type and parameters
Definition task.h:216
int kill(enum EN_TASK_STATUS status, void *priv_data) override
Definition task.h:661
static void * sub_buffer_offset(void *in, size_t off)
Definition task.h:709
coroutine_type coroutine_t
Definition task.h:54
coroutine_type::ptr_type & get_coroutine_context() LIBCOPP_MACRO_NOEXCEPT
Definition task.h:498
bool is_completed() const LIBCOPP_MACRO_NOEXCEPT override
Definition task.h:691
static ptr_type create(Ty(TInst::*func), TInst *instance, size_t stack_size=0, size_t private_buffer_size=0)
Definition task.h:203
LIBCOPP_COPP_NAMESPACE_ID::util::lock::atomic_int_type< LIBCOPP_COPP_NAMESPACE_ID::util::lock::unsafe_int_type< size_t > > ref_count_
Definition task.h:991
ptr_type ptr_t
Definition task.h:53
LIBCOPP_COPP_NAMESPACE_ID::container_value_type< TAWAITABLE > await_value(TAWAITABLE &&awaitable) noexcept(std::is_nothrow_copy_constructible< LIBCOPP_COPP_NAMESPACE_ID::container_value_type< TAWAITABLE > >::value &&noexcept(LIBCOPP_COPP_NAMESPACE_ID::stackful_channel_error_transform< LIBCOPP_COPP_NAMESPACE_ID::container_value_type< TAWAITABLE > >()(LIBCOPP_COPP_NAMESPACE_ID::COPP_EC_ARGS_ERROR)))
Waits for the specified awaitable object to finish and retrieves its result.
Definition task.h:399
ptr_type then(Ty &&functor, void *priv_data=nullptr)
create next task with functor using the same allocator and private buffer size as this task
Definition task.h:440
size_t stack_size_
Definition task.h:979
static LIBCOPP_COTASK_API_HEAD_ONLY ptr_type create_with_delegate(Ty &&callable, typename coroutine_type::allocator_type &alloc, size_t stack_size=0, size_t private_buffer_size=0)
create task with functor
Definition task.h:92
TCO_MACRO macro_coroutine_type
Definition task.h:40
LIBCOPP_COPP_NAMESPACE_ID::container_value_type< TAWAITABLE > await_value(TAWAITABLE &&awaitable, TERROR_TRANSFORM &&error_transform) noexcept(std::is_nothrow_copy_constructible< LIBCOPP_COPP_NAMESPACE_ID::container_value_type< TAWAITABLE > >::value &&noexcept(error_transform(LIBCOPP_COPP_NAMESPACE_ID::COPP_EC_ARGS_ERROR)))
Waits for the specified awaitable object to finish and retrieves its result.
Definition task.h:378
LIBCOPP_COPP_NAMESPACE_ID::memory::intrusive_ptr< self_type > ptr_type
Definition task.h:42
ptr_type then(ptr_type next_task, void *priv_data=nullptr)
add task to run when task finished
Definition task.h:430
static self_type * this_task()
Definition task.h:463
virtual ~task()
Definition task.h:472
static ptr_type create(Ty(*func)(void *), typename coroutine_type::allocator_type &alloc, size_t stack_size=0, size_t private_buffer_size=0)
create task with function
Definition task.h:174
ptr_type next(Ty(TInst::*func), TInst *instance, typename coroutine_type::allocator_type &alloc, void *priv_data=nullptr, size_t stack_size=0, size_t private_buffer_size=0)
Definition task.h:314
typename macro_coroutine_type::coroutine_type coroutine_type
Definition task.h:44
size_t use_count() const
Definition task.h:729
task(const task &)=delete
int await_task(ptr_type wait_task)
await_task another cotask to finish
Definition task.h:327
static void * add_buffer_offset(void *in, size_t off)
Definition task.h:705
int start(void *priv_data, EN_TASK_STATUS expected_status=EN_TS_CREATED) override
Definition task.h:523
coroutine_type::ptr_type coroutine_obj_
Definition task.h:980
friend void intrusive_ptr_add_ref(self_type *p)
Definition task.h:833
ptr_type next(Ty &&functor, void *priv_data=nullptr, size_t stack_size=0, size_t private_buffer_size=0)
create next task with functor
Definition task.h:268
int cancel(void *priv_data) override
Definition task.h:627
id_type id_t
Definition task.h:57
typename impl::task_impl::id_allocator_type id_allocator_type
Definition task.h:48
typename macro_coroutine_type::stack_allocator_type stack_allocator_type
Definition task.h:45
ptr_type next(Ty(*func)(void *), typename coroutine_type::allocator_type &alloc, void *priv_data=nullptr, size_t stack_size=0, size_t private_buffer_size=0)
Definition task.h:293
int resume(void *priv_data, EN_TASK_STATUS expected_status=EN_TS_WAITING) override
Definition task.h:604
id_allocator_type id_allocator_t
Definition task.h:56
static LIBCOPP_COTASK_API_HEAD_ONLY ptr_type create(Ty(TInst::*func), TInst *instance, typename coroutine_type::allocator_type &alloc, size_t stack_size=0, size_t private_buffer_size=0)
create task with function
Definition task.h:194
task_group next_list_
Definition task.h:981
#define LIBCOPP_UTIL_FORCEINLINE
#define LIBCOPP_UTIL_LIKELY_CONDITION(__C)
#define LIBCOPP_UTIL_UNLIKELY_CONDITION(__C)
#define LIBCOPP_MACRO_STD_COROUTINE_NAMESPACE
Definition coroutine.h:41
std::shared_ptr< cli::cmd_option_value > value_type
Definition cmd_option.h:50
static LIBCOPP_COPP_API_HEAD_ONLY int resume(void *invoke_task, stackful_channel_context_base *priv_data)
Definition task.h:1018
std::list< std::pair< ptr_type, void * > > member_list_
Definition task.h:65
LIBCOPP_COTASK_API_HEAD_ONLY placement_destroy_fn_t get_placement_destroy(task_action_functor< Ty > *)
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
@ EN_TS_INVALID
Definition task_impl.h:30
@ EN_TS_WAITING
Definition task_impl.h:33
@ EN_TS_RUNNING
Definition task_impl.h:32
@ EN_TS_CANCELED
Definition task_impl.h:35
class LIBCOPP_COTASK_API_HEAD_ONLY task_manager