/* This code is taken from libpcl library. * Rip-off done by stsp for dosemu2 project. * Original copyrights below. */ /* * PCL by Davide Libenzi (Portable Coroutine Library) * Copyright (C) 2003..2010 Davide Libenzi * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Davide Libenzi * */ #define WANT_ASAN 1 #ifdef __clang__ #define HAS_FEATURE(x) __has_feature(x) #else #define HAS_FEATURE(x) 0 #endif #if (defined(__SANITIZE_ADDRESS__) || HAS_FEATURE(address_sanitizer)) && WANT_ASAN #define USE_ASAN 1 #else #define USE_ASAN 0 #endif #if USE_ASAN #include #endif #include #include #include "pcl.h" #include "pcl_private.h" #include "pcl_ctx.h" static cothread_ctx *co_get_thread_ctx(coroutine *co); static void co_switch_context(co_base *octx, co_base *nctx) { #if USE_ASAN void *fake_stack_save = NULL; __sanitizer_start_switch_fiber(octx->exited ? NULL : &fake_stack_save, nctx->stack, nctx->stack_size); nctx->tmp = fake_stack_save; #endif if (octx->ctx.ops->swap_context(&octx->ctx, nctx->ctx.cc) < 0) { fprintf(stderr, "[PCL] Context switch failed\n"); exit(1); } #if USE_ASAN __sanitizer_finish_switch_fiber(fake_stack_save, NULL, NULL); #endif } static void co_runner(void *arg) { coroutine *co = arg; cothread_ctx *tctx; #if USE_ASAN __sanitizer_finish_switch_fiber(co->tmp, NULL, NULL); #endif tctx = co_get_thread_ctx(co); co->restarget = co->caller; co->func(co->data); co_exit(tctx); abort(); } static coroutine *do_co_create(void (*func)(void *), void *data, void *stack, int size, int corosize) { int alloc = 0; coroutine *co; if ((size &= ~(sizeof(long) - 1)) < CO_MIN_SIZE) return NULL; if (stack == NULL) { size = CO_STK_COROSIZE(size + corosize); stack = malloc(size); if (stack == NULL) return NULL; alloc = size; } co = stack; co->stack = (char *) stack + CO_STK_COROSIZE(corosize); co->stack_size = size; co->alloc = alloc; co->func = func; co->data = data; co->exited = 0; return co; } coroutine_t co_create(cohandle_t handle, void (*func)(void *), void *data, void *stack, int size) { coroutine *co; cothread_ctx *tctx = (cothread_ctx *)handle; co = do_co_create(func, data, stack, size, tctx->ctx_sizeof); if (!co) return NULL; co->ctx = tctx->co_main.ctx; co->ctx.cc = co->stk; co->ctx_main = tctx; if (co->ctx.ops->create_context(&co->ctx, co_runner, co, co->stack, size - CO_STK_COROSIZE(tctx->ctx_sizeof)) < 0) { if (co->alloc) free(co); return NULL; } return (coroutine_t) co; } void co_delete(coroutine_t coro) { coroutine *co = (coroutine *) coro; cothread_ctx *tctx = co_get_thread_ctx(co); if ((co_base *)co == tctx->co_curr) { fprintf(stderr, "[PCL] Cannot delete itself: curr=%p\n", tctx->co_curr); exit(1); } if (co->alloc) free(co); } void co_call(coroutine_t coro) { coroutine *co = (coroutine *) coro; cothread_ctx *tctx = co_get_thread_ctx(co); co_base *oldco = tctx->co_curr; co->caller = tctx->co_curr; tctx->co_curr = co; co_switch_context(oldco, co); if (co->exited) co_delete(co); } void co_resume(cohandle_t handle) { cothread_ctx *tctx = (cothread_ctx *)handle; co_call(tctx->co_curr->restarget); tctx->co_curr->restarget = tctx->co_curr->caller; } void co_exit(cohandle_t handle) { cothread_ctx *tctx = (cothread_ctx *)handle; co_base *newco = tctx->co_curr->restarget, *co = tctx->co_curr; co->exited = 1; tctx->co_curr = newco; co_switch_context(co, newco); } coroutine_t co_current(cohandle_t handle) { cothread_ctx *tctx = (cothread_ctx *)handle; return (coroutine_t) tctx->co_curr; } void *co_get_data(coroutine_t coro) { coroutine *co = (coroutine *) coro; return co->data; } void *co_set_data(coroutine_t coro, void *data) { coroutine *co = (coroutine *) coro; void *odata; odata = co->data; co->data = data; return odata; } /* * Simple case, the single thread one ... */ static void do_co_init(cothread_ctx *tctx) { tctx->co_main.ctx.cc = tctx->stk0; tctx->co_main.ctx_main = tctx; tctx->co_main.exited = 0; tctx->co_curr = &tctx->co_main; } cohandle_t co_thread_init(enum CoBackend b) { int sz = ctx_sizeof(b); cothread_ctx *tctx = malloc(sizeof(cothread_ctx) + CO_STK_ALIGN(sz)); do_co_init(tctx); ctx_init(b, &tctx->co_main.ctx.ops); tctx->ctx_sizeof = sz; return tctx; } void co_thread_cleanup(cohandle_t handle) { cothread_ctx *tctx = (cothread_ctx *)handle; free(tctx); } static cothread_ctx *co_get_thread_ctx(coroutine *co) { return co->ctx_main; }