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) {