void co_resume(void) { cothread_ctx *tctx = co_get_thread_ctx(); co_call(tctx->co_curr->restarget); tctx->co_curr->restarget = tctx->co_curr->caller; }
static void co_runner(void) { cothread_ctx *tctx = co_get_thread_ctx(); coroutine *co = tctx->co_curr; co->restarget = co->caller; co->func(co->data); co_exit(); }
void co_call(coroutine_t coro) { cothread_ctx *tctx = co_get_thread_ctx(); coroutine *co = (coroutine *) coro, *oldco = tctx->co_curr; co->caller = tctx->co_curr; tctx->co_curr = co; co_switch_context(&oldco->ctx, &co->ctx); }
static void co_switch_context(co_ctx_t *octx, co_ctx_t *nctx) { cothread_ctx *tctx = co_get_thread_ctx(); if (swapcontext(&octx->cc, &nctx->cc) < 0) { fprintf(stderr, "[PCL] Context switch failed: curr=%p\n", tctx->co_curr); exit(1); } }
void co_delete(coroutine_t coro) { cothread_ctx *tctx = co_get_thread_ctx(); coroutine *co = (coroutine *) coro; if (co == tctx->co_curr) { fprintf(stderr, "[PCL] Cannot delete itself: curr=%p\n", tctx->co_curr); exit(1); } if (co->alloc) free(co); }
static void co_del_helper(void *data) { cothread_ctx *tctx; coroutine *cdh; for (;;) { tctx = co_get_thread_ctx(); cdh = tctx->co_dhelper; tctx->co_dhelper = NULL; co_delete(tctx->co_curr->caller); co_call((coroutine_t) cdh); if (tctx->co_dhelper == NULL) { fprintf(stderr, "[PCL] Resume to delete helper coroutine: curr=%p caller=%p\n", tctx->co_curr, tctx->co_curr->caller); exit(1); } } }
void co_exit_to(coroutine_t coro) { cothread_ctx *tctx = co_get_thread_ctx(); coroutine *co = (coroutine *) coro; if (tctx->dchelper == NULL && (tctx->dchelper = co_create(co_del_helper, NULL, tctx->stk, sizeof(tctx->stk))) == NULL) { fprintf(stderr, "[PCL] Unable to create delete helper coroutine: curr=%p\n", tctx->co_curr); exit(1); } tctx->co_dhelper = co; co_call((coroutine_t) tctx->dchelper); fprintf(stderr, "[PCL] Stale coroutine called: curr=%p exitto=%p caller=%p\n", tctx->co_curr, co, tctx->co_curr->caller); exit(1); }
/* * This code comes from the GNU Pth implementation and uses the * sigstack/sigaltstack() trick. * * The ingenious fact is that this variant runs really on _all_ POSIX * compliant systems without special platform kludges. But be _VERY_ * carefully when you change something in the following code. The slightest * change or reordering can lead to horribly broken code. Really every * function call in the following case is intended to be how it is, doubt * me... * * For more details we strongly recommend you to read the companion * paper ``Portable Multithreading -- The Signal Stack Trick for * User-Space Thread Creation'' from Ralf S. Engelschall. */ static void co_ctx_bootstrap(void) { cothread_ctx *tctx = co_get_thread_ctx(); co_ctx_t * volatile ctx_starting; void (* volatile ctx_starting_func)(void); /* * Switch to the final signal mask (inherited from parent) */ sigprocmask(SIG_SETMASK, &ctx_creating_sigs, NULL); /* * Move startup details from static storage to local auto * variables which is necessary because it has to survive in * a local context until the thread is scheduled for real. */ ctx_starting = ctx_creating; ctx_starting_func = (void (*)(void)) ctx_creating_func; /* * Save current machine state (on new stack) and * go back to caller until we're scheduled for real... */ if (!setjmp(ctx_starting->cc)) longjmp(ctx_caller.cc, 1); /* * The new thread is now running: GREAT! * Now we just invoke its init function.... */ ctx_starting_func(); fprintf(stderr, "[PCL] Hmm, you really shouldn't reach this point: curr=%p\n", tctx->co_curr); exit(1); }
coroutine_t co_current(void) { cothread_ctx *tctx = co_get_thread_ctx(); return (coroutine_t) tctx->co_curr; }
void co_exit(void) { cothread_ctx *tctx = co_get_thread_ctx(); co_exit_to((coroutine_t) tctx->co_curr->restarget); }