Skip to content

Commit

Permalink
修订原子操作compare_exchange_weak失败会导致cotask提前结束的问题
Browse files Browse the repository at this point in the history
增加一系列API用以检查next成功与否和超时、正在退出的判定
  • Loading branch information
owent committed Mar 2, 2018
1 parent c3ba5a2 commit faec9aa
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 11 deletions.
9 changes: 9 additions & 0 deletions include/libcotask/impl/task_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,15 @@ namespace cotask {
virtual bool is_canceled() const UTIL_CONFIG_NOEXCEPT;
virtual bool is_completed() const UTIL_CONFIG_NOEXCEPT;
virtual bool is_faulted() const UTIL_CONFIG_NOEXCEPT;
virtual bool is_timeout() const UTIL_CONFIG_NOEXCEPT;
/**
* @brief check if a cotask is exiting
* @note cotask is exiting means the cotask is is_completed() or is killed.
* if a cotask is killed and is running, then is_completed() == false but is_exiting() == true,
* and after the cotask finished, is_completed() == true
* @return return true if a cotask is exiting.
*/
bool is_exiting() const UTIL_CONFIG_NOEXCEPT;

public:
virtual int get_ret_code() const = 0;
Expand Down
29 changes: 19 additions & 10 deletions include/libcotask/task.h
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ namespace cotask {
* @see impl::task_impl::next
* @param next_task next stack
* @param priv_data priv_data passed to resume or start next stack
* @return next_task
* @return next_task if success , or self if failed
*/
inline ptr_t next(ptr_t next_task, void *priv_data = UTIL_CONFIG_NULLPTR) {
// can not refers to self
Expand All @@ -236,8 +236,8 @@ namespace cotask {
}

// can not add next task when finished
if (get_status() >= EN_TS_DONE) {
return next_task;
if (is_exiting()) {
return ptr_t(this);
}

#if !defined(PROJECT_DISABLE_MT) || !(PROJECT_DISABLE_MT)
Expand All @@ -254,7 +254,7 @@ namespace cotask {
* @param functor
* @param priv_data priv_data passed to start functor
* @param stack_size stack size
* @return task smart pointer
* @return the created task if success , or self if failed
*/
#if defined(UTIL_CONFIG_COMPILER_CXX_RVALUE_REFERENCES) && UTIL_CONFIG_COMPILER_CXX_RVALUE_REFERENCES
template <typename Ty>
Expand Down Expand Up @@ -286,7 +286,7 @@ namespace cotask {
* @param func function
* @param priv_data priv_data passed to start function
* @param stack_size stack size
* @return task smart pointer
* @return the created task if success , or self if failed
*/
template <typename Ty>
inline ptr_t next(Ty (*func)(void *), void *priv_data = UTIL_CONFIG_NULLPTR, size_t stack_size = 0,
Expand All @@ -307,7 +307,7 @@ namespace cotask {
* @param instance instance
* @param priv_data priv_data passed to start (instance->*func)(priv_data)
* @param stack_size stack size
* @return task smart pointer
* @return the created task if success , or self if failed
*/
template <typename Ty, typename TInst>
inline ptr_t next(Ty(TInst::*func), TInst *instance, void *priv_data = UTIL_CONFIG_NULLPTR, size_t stack_size = 0,
Expand Down Expand Up @@ -390,10 +390,19 @@ namespace cotask {

finish_priv_data_ = priv_data;
_notify_finished(priv_data);
} else if (likely(_cas_status(from_status, EN_TS_WAITING))) { // Atomic.CAS here
// waiting
} else { // canceled or killed
_notify_finished(finish_priv_data_);
return ret;
}

while (true) {
if (from_status >= EN_TS_DONE) { // canceled or killed
_notify_finished(finish_priv_data_);
break;
}

if (likely(_cas_status(from_status, EN_TS_WAITING))) { // Atomic.CAS here
break;
// waiting
}
}

return ret;
Expand Down
6 changes: 5 additions & 1 deletion src/libcotask/impl/task_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,14 @@ namespace cotask {

bool task_impl::is_canceled() const UTIL_CONFIG_NOEXCEPT { return EN_TS_CANCELED == get_status(); }

bool task_impl::is_completed() const UTIL_CONFIG_NOEXCEPT { return EN_TS_DONE <= get_status(); }
bool task_impl::is_completed() const UTIL_CONFIG_NOEXCEPT { return is_exiting(); }

bool task_impl::is_faulted() const UTIL_CONFIG_NOEXCEPT { return EN_TS_KILLED <= get_status(); }

bool task_impl::is_timeout() const UTIL_CONFIG_NOEXCEPT { return EN_TS_TIMEOUT == get_status(); }

bool task_impl::is_exiting() const UTIL_CONFIG_NOEXCEPT { return EN_TS_DONE <= get_status(); }

int task_impl::on_finished() { return 0; }

task_impl *task_impl::this_task() {
Expand Down
31 changes: 31 additions & 0 deletions test/case/coroutine_task_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -388,5 +388,36 @@ CASE_TEST(coroutine_task, priavte_buffer) {
CASE_EXPECT_EQ(0, co_task->start());
}

static int test_context_task_timeout(void *) {

cotask::task<>::this_task()->yield();

CASE_EXPECT_TRUE(cotask::task<>::this_task()->is_timeout());
CASE_EXPECT_TRUE(cotask::task<>::this_task()->is_faulted());
CASE_EXPECT_FALSE(cotask::task<>::this_task()->is_completed());
CASE_EXPECT_TRUE(cotask::task<>::this_task()->is_exiting());

return 0;
}

CASE_TEST(coroutine_task, kill_and_timeout) {
typedef cotask::task<>::ptr_t task_ptr_type;
task_ptr_type co_task = cotask::task<>::create(test_context_task_timeout, 16384, 256);

void *priv_data = co_task->get_private_buffer();
memset(priv_data, 0x5e, 256);

CASE_EXPECT_EQ(0, co_task->start());

CASE_EXPECT_FALSE(co_task->is_timeout());
CASE_EXPECT_FALSE(co_task->is_faulted());
CASE_EXPECT_FALSE(co_task->is_completed());
CASE_EXPECT_FALSE(co_task->is_exiting());

CASE_EXPECT_EQ(0, co_task->kill(cotask::EN_TS_TIMEOUT, NULL));

CASE_EXPECT_TRUE(co_task->is_completed());
}


#endif

0 comments on commit faec9aa

Please sign in to comment.