diff --git a/amd64.c b/amd64.c index 15468090..ab62bd92 100644 --- a/amd64.c +++ b/amd64.c @@ -120,6 +120,24 @@ cothread_t co_active() { return co_active_handle; } +cothread_t co_derive(void* memory, unsigned int size, void (*entrypoint)(void)) { + cothread_t handle; + if(!co_swap) { + co_init(); + co_swap = (void (*)(cothread_t, cothread_t))co_swap_function; + } + if(!co_active_handle) co_active_handle = &co_active_buffer; + + if(handle = (cothread_t)memory) { + long long *p = (long long*)((char*)handle + size); /* seek to top of stack */ + *--p = (long long)crash; /* crash if entrypoint returns */ + *--p = (long long)entrypoint; /* start of function */ + *(long long*)handle = (long long)p; /* stack pointer */ + } + + return handle; +} + cothread_t co_create(unsigned int size, void (*entrypoint)(void)) { cothread_t handle; if(!co_swap) { diff --git a/arm.c b/arm.c index 313f1134..20c95310 100644 --- a/arm.c +++ b/arm.c @@ -40,8 +40,25 @@ cothread_t co_active() { return co_active_handle; } +cothread_t co_derive(void* memory, unsigned int size, void (*entrypoint)(void)) { + unsigned long* handle; + if(!co_swap) { + co_init(); + co_swap = (void (*)(cothread_t, cothread_t))co_swap_function; + } + if(!co_active_handle) co_active_handle = &co_active_buffer; + + if(handle = (unsigned long*)memory) { + unsigned long* p = (unsigned long*)((unsigned char*)handle + size); + handle[8] = (unsigned long)p; + handle[9] = (unsigned long)entrypoint; + } + + return handle; +} + cothread_t co_create(unsigned int size, void (*entrypoint)(void)) { - unsigned long* handle = 0; + unsigned long* handle; if(!co_swap) { co_init(); co_swap = (void (*)(cothread_t, cothread_t))co_swap_function; diff --git a/doc/usage.html b/doc/usage.html index 38576dc5..994072f9 100644 --- a/doc/usage.html +++ b/doc/usage.html @@ -62,6 +62,14 @@ Return handle to current cothread. Always returns a valid handle, even when called from the main program thread.
+cothread_t co_derive(void* memory, unsigned int heapsize, void (*coentry)(void));

+Initializes new cothread.
+This function is identical to co_create, only it attempts to use the provided +memory instead of allocating new memory on the heap. Please note that certain +implementations (currently only Windows Fibers) cannot be created using existing +memory, and as such, this function will fail. +
+ cothread_t co_create(unsigned int heapsize, void (*coentry)(void));

Create new cothread.
Heapsize is the amount of memory allocated for the cothread stack, specified diff --git a/fiber.c b/fiber.c index 50af4a72..bdf4dd4e 100644 --- a/fiber.c +++ b/fiber.c @@ -24,6 +24,11 @@ cothread_t co_active() { return co_active_; } +cothread_t co_derive(void* memory, unsigned int heapsize, void (*coentry)(void)) { + //Windows fibers do not allow users to supply their own memory + return (cothread_t)0; +} + cothread_t co_create(unsigned int heapsize, void (*coentry)(void)) { if(!co_active_) { ConvertThreadToFiber(0); diff --git a/libco.h b/libco.h index c8452154..b7f60852 100644 --- a/libco.h +++ b/libco.h @@ -1,5 +1,5 @@ /* - libco v18.02 (2017-11-06) + libco v19 (2019-02-18) author: byuu license: ISC */ @@ -14,6 +14,7 @@ extern "C" { typedef void* cothread_t; cothread_t co_active(); +cothread_t co_derive(void*, unsigned int, void (*)(void)); cothread_t co_create(unsigned int, void (*)(void)); void co_delete(cothread_t); void co_switch(cothread_t); diff --git a/ppc.c b/ppc.c index 6d79b44f..f071fab2 100644 --- a/ppc.c +++ b/ppc.c @@ -258,6 +258,70 @@ static const uint32_t libco_ppc_code[1024] = { #define CO_SWAP_ASM(x, y) ((void (*)(cothread_t, cothread_t))(uintptr_t)libco_ppc_code)(x, y) #endif +static uint32_t* co_derive_(void* memory, unsigned size, uintptr_t entry) { + (void)entry; + + uint32_t* t = (uint32_t*)memory; + + #if LIBCO_PPCDESC + if(t) { + memcpy(t, (void*)entry, sizeof(void*) * 3); /* copy entry's descriptor */ + *(const void**)t = libco_ppc_code; /* set function pointer to swap routine */ + } + #endif + + return t; +} + +cothread_t co_derive(void* memory, unsigned int size, void (*entry_)(void)) { + uintptr_t entry = (uintptr_t)entry_; + uint32_t* t = 0; + + /* be sure main thread was successfully allocated */ + if(co_active()) { + t = co_derive_(memory, size, entry); + } + + if(t) { + uintptr_t sp; + int shift; + + /* save current registers into new thread, so that any special ones will have proper values when thread is begun */ + CO_SWAP_ASM(t, t); + + #if LIBCO_PPCDESC + entry = (uintptr_t)*(void**)entry; /* get real address */ + #endif + + /* put stack near end of block, and align */ + sp = (uintptr_t)t + size - above_stack; + sp -= sp % stack_align; + + /* on PPC32, we save and restore GPRs as 32 bits. for PPC64, we + save and restore them as 64 bits, regardless of the size the ABI + uses. so, we manually write pointers at the proper size. we always + save and restore at the same address, and since PPC is big-endian, + we must put the low byte first on PPC32. */ + + /* if uintptr_t is 32 bits, >>32 is undefined behavior, + so we do two shifts and don't have to care how many bits uintptr_t is. */ + #if LIBCO_PPC64 + shift = 16; + #else + shift = 0; + #endif + + /* set up so entry will be called on next swap */ + t[ 8] = (uint32_t)(entry >> shift >> shift); + t[ 9] = (uint32_t)entry; + + t[10] = (uint32_t)(sp >> shift >> shift); + t[11] = (uint32_t)sp; + } + + return t; +} + static uint32_t* co_create_(unsigned size, uintptr_t entry) { (void)entry; diff --git a/sjlj.c b/sjlj.c index 1d0cb59a..d99572a3 100644 --- a/sjlj.c +++ b/sjlj.c @@ -38,10 +38,52 @@ cothread_t co_active() { return (cothread_t)co_running; } +cothread_t co_derive(void* memory, unsigned int size, void (*coentry)(void)) { + if(!co_running) co_running = &co_primary; + + cothread_struct* thread = (cothread_struct*)memory; + memory = (unsigned char*)memory + sizeof(cothread_struct); + size -= sizeof(cothread_struct); + if(thread) { + struct sigaction handler; + struct sigaction old_handler; + + stack_t stack; + stack_t old_stack; + + thread->coentry = thread->stack = 0; + + stack.ss_flags = 0; + stack.ss_size = size; + thread->stack = stack.ss_sp = memory; + if(stack.ss_sp && !sigaltstack(&stack, &old_stack)) { + handler.sa_handler = springboard; + handler.sa_flags = SA_ONSTACK; + sigemptyset(&handler.sa_mask); + creating = thread; + + if(!sigaction(SIGUSR1, &handler, &old_handler)) { + if(!raise(SIGUSR1)) { + thread->coentry = coentry; + } + sigaltstack(&old_stack, 0); + sigaction(SIGUSR1, &old_handler, 0); + } + } + + if(thread->coentry != coentry) { + co_delete(thread); + thread = 0; + } + } + + return (cothread_t)thread; +} + cothread_t co_create(unsigned int size, void (*coentry)(void)) { if(!co_running) co_running = &co_primary; - cothread_struct *thread = (cothread_struct*)malloc(sizeof(cothread_struct)); + cothread_struct* thread = (cothread_struct*)malloc(sizeof(cothread_struct)); if(thread) { struct sigaction handler; struct sigaction old_handler; diff --git a/ucontext.c b/ucontext.c index 9ba47c88..edf513d4 100644 --- a/ucontext.c +++ b/ucontext.c @@ -31,6 +31,23 @@ cothread_t co_active() { return (cothread_t)co_running; } +cothread_t co_derive(void* memory, unsigned int heapsize, void (*coentry)(void)) { + if(!co_running) co_running = &co_primary; + ucontext_t* thread = (ucontext_t*)memory; + memory = (unsigned char*)memory + sizeof(ucontext_t); + heapsize -= sizeof(ucontext_t); + if(thread) { + if((!getcontext(thread) && !(thread->uc_stack.ss_sp = 0)) && (thread->uc_stack.ss_sp = memory)) { + thread->uc_link = co_running; + thread->uc_stack.ss_size = heapsize; + makecontext(thread, coentry, 0); + } else { + thread = 0; + } + } + return (cothread_t)thread; +} + cothread_t co_create(unsigned int heapsize, void (*coentry)(void)) { if(!co_running) co_running = &co_primary; ucontext_t* thread = (ucontext_t*)malloc(sizeof(ucontext_t)); diff --git a/x86.c b/x86.c index b5c38216..d79306c6 100644 --- a/x86.c +++ b/x86.c @@ -74,6 +74,24 @@ cothread_t co_active() { return co_active_handle; } +cothread_t co_derive(void* memory, unsigned int size, void (*entrypoint)(void)) { + cothread_t handle; + if(!co_swap) { + co_init(); + co_swap = (void (fastcall*)(cothread_t, cothread_t))co_swap_function; + } + if(!co_active_handle) co_active_handle = &co_active_buffer; + + if(handle = (cothread_t)memory) { + long *p = (long*)((char*)handle + size); /* seek to top of stack */ + *--p = (long)crash; /* crash if entrypoint returns */ + *--p = (long)entrypoint; /* start of function */ + *(long*)handle = (long)p; /* stack pointer */ + } + + return handle; +} + cothread_t co_create(unsigned int size, void (*entrypoint)(void)) { cothread_t handle; if(!co_swap) {