近期的一个协程流程BUG

发表于:2017-1-05 11:25

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:I'm OWenT    来源:51Testing软件测试网采编

  最近一直没什么时间整理近期碰到的问题,今天思考了一下之前碰到的一个临时处理的BUG,顺便写点东西清理一下思路。
  其实严格来说这个BUG更应该是一个流程试用问题,不过这个问题应该是需要能在协程库里检测并抛出错误来。
  libcopp 的执行流程
  这个BUG涉及最底层context的执行流程,这个协程库切入有两个接口(start和resume),截止目前为止,这两个接口其实是对等的,然后有一个切出接口(yield)。切入的时候需要记录调用者的执行上下文,用于在切出时上下文切出到哪里。问题是各种情况下的这个上下文保存的执行状态。
  然后再来看上下文切换的代码,第一部分是首次切入的入口(调用start函数)。
void coroutine_context_base::coroutine_context_callback(::copp::fcontext::transfer_t src_ctx) {
assert(src_ctx.data);
if (NULL == src_ctx.data) {
abort();
return;
}
// copy jump_src_data_t in case it's destroyed later
jump_src_data_t jump_src = *reinterpret_cast<jump_src_data_t *>(src_ctx.data);
// this must in a coroutine
coroutine_context_base *ins_ptr = jump_src.to_co;
assert(ins_ptr);
if (NULL == ins_ptr) {
abort();
return;
}
// update caller of to_co
ins_ptr->caller_ = src_ctx.fctx;
// save from_co's fcontext and switch status
if (UTIL_CONFIG_NULLPTR != jump_src.from_co) {
jump_src.from_co->callee_ = src_ctx.fctx;
int from_status = status_t::EN_CRS_RUNNING; // from coroutine change status from running to ready
jump_src.from_co->status_.compare_exchange_strong(from_status, status_t::EN_CRS_READY);
}
// this_coroutine
set_this_coroutine_context(ins_ptr);
// run logic code
ins_ptr->run_and_recv_retcode(jump_src.priv_data);
ins_ptr->status_.store(status_t::EN_CRS_FINISHED);
// jump back to caller
ins_ptr->yield();
}
  最重要的是 save from_co’s fcontext and switch status 下面的这一段。这里的作用就是切入后要把跳入前的上下文保存到来源的协程中。
  然后是恢复的流程(调用resume/yield函数):
void coroutine_context_base::jump_to(fcontext::fcontext_t &to_fctx, stack_context &from_sctx, stack_context &to_sctx,
jump_src_data_t &jump_transfer) UTIL_CONFIG_NOEXCEPT {
copp::fcontext::transfer_t res;
jump_src_data_t *jump_src;
int from_status;
bool swap_success;
// can not use any more stack now
// can not initialize those vars here
#ifdef COPP_MACRO_USE_SEGMENTED_STACKS
assert(&from_sctx != &to_sctx);
// ROOT->A: jump_transfer.from_co == NULL, jump_transfer.to_co == A, from_sctx == A.caller_stack_, skip backup segments
// A->B.start(): jump_transfer.from_co == A, jump_transfer.to_co == B, from_sctx == B.caller_stack_, backup segments
// B.yield()->A: jump_transfer.from_co == B, jump_transfer.to_co == NULL, from_sctx == B.callee_stack_, skip backup segments
if (UTIL_CONFIG_NULLPTR != jump_transfer.from_co) {
__splitstack_getcontext(jump_transfer.from_co->callee_stack_.segments_ctx);
if (&from_sctx != &jump_transfer.from_co->callee_stack_) {
memcpy(&from_sctx.segments_ctx, &jump_transfer.from_co->callee_stack_.segments_ctx, sizeof(from_sctx.segments_ctx));
}
} else {
__splitstack_getcontext(from_sctx.segments_ctx);
}
__splitstack_setcontext(to_sctx.segments_ctx);
#endif
res = copp::fcontext::copp_jump_fcontext(to_fctx, &jump_transfer);
if (NULL == res.data) {
abort();
return;
}
jump_src = reinterpret_cast<jump_src_data_t *>(res.data);
assert(jump_src);
/**
* save from_co's fcontext and switch status
* we should use from_co in transfer_t, because it may not jump from jump_transfer.to_co
*
* if we jump sequence is A->B->C->A.resume(), and if this call is A->B, then
* jump_src->from_co = C, jump_src->to_co = A, jump_transfer.from_co = A, jump_transfer.to_co = B
* and now we should save the callee of C and set the caller of A = C
*
* if we jump sequence is A->B.yield()->A, and if this call is A->B, then
* jump_src->from_co = B, jump_src->to_co = NULL, jump_transfer.from_co = A, jump_transfer.to_co = B
* and now we should save the callee of B and should change the caller of A
*
*/
// update caller of to_co if not jump from yield mode
if (UTIL_CONFIG_NULLPTR != jump_src->to_co) {
jump_src->to_co->caller_ = res.fctx;
}
if (UTIL_CONFIG_NULLPTR != jump_src->from_co) {
jump_src->from_co->callee_ = res.fctx;
from_status = jump_src->from_co->status_.load();
if (status_t::EN_CRS_RUNNING == from_status) {
jump_src->from_co->status_.compare_exchange_strong(from_status, status_t::EN_CRS_READY);
} else if (status_t::EN_CRS_FINISHED == from_status) {
// if in finished status, change it to exited
jump_src->from_co->status_.store(status_t::EN_CRS_EXITED);
}
}
// private data
jump_transfer.priv_data = jump_src->priv_data;
// this_coroutine
set_this_coroutine_context(jump_transfer.from_co);
// resume running status of from_co
if (NULL != jump_transfer.from_co) {
from_status = jump_transfer.from_co->status_.load();
swap_success = false;
while (!swap_success && status_t::EN_CRS_READY == from_status) {
swap_success = jump_transfer.from_co->status_.compare_exchange_strong(from_status, status_t::EN_CRS_RUNNING);
}
}
}
21/212>
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号