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