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);
}
}
}
同样,需要关注的部分是update caller of to_co if not jump from yield mode后面的保存上下下文的部分。