diff --git a/fiber.c b/fiber.c new file mode 100644 index 00000000..d1b39586 --- /dev/null +++ b/fiber.c @@ -0,0 +1,51 @@ +/* + libco.win (2008-01-28) + authors: Nach, byuu + license: public domain +*/ + +#define LIBCO_C +#include "../libco.h" +#define WINVER 0x0400 +#define _WIN32_WINNT 0x0400 +#define WIN32_LEAN_AND_MEAN +#include + +#ifdef __cplusplus +extern "C" { +#endif + +static thread_local cothread_t co_active_ = 0; + +static void __stdcall co_thunk(void *coentry) { + ((void (*)(void))coentry)(); +} + +cothread_t co_active() { + if(!co_active_) { + ConvertThreadToFiber(0); + co_active_ = GetCurrentFiber(); + } + return co_active_; +} + +cothread_t co_create(unsigned int heapsize, void (*coentry)(void)) { + if(!co_active_) { + ConvertThreadToFiber(0); + co_active_ = GetCurrentFiber(); + } + return (cothread_t)CreateFiber(heapsize, co_thunk, (void*)coentry); +} + +void co_delete(cothread_t cothread) { + DeleteFiber(cothread); +} + +void co_switch(cothread_t cothread) { + co_active_ = cothread; + SwitchToFiber(cothread); +} + +#ifdef __cplusplus +} +#endif diff --git a/libco.ucontext.cpp b/libco.ucontext.cpp deleted file mode 100644 index da236326..00000000 --- a/libco.ucontext.cpp +++ /dev/null @@ -1,80 +0,0 @@ -/* - libco.ucontext (2007-09-08) - author: byuu - license: public domain -*/ - -#include -#include -#include "../libco.h" - -//WARNING: the overhead of POSIX ucontext is very high, -//averaging ~450x that of standard subroutine calls. -//(tested on FreeBSD 6.2-RELEASE) -//By contrast, on the same system, libco_x86's overhead -//is ~7.25x standard subroutine calls; or fifty times faster. -// -//This library only exists for two reasons: -//1 - as an initial test for the viability of a ucontext implementation -//2 - to demonstrate the power and speed of libco over existing implementations, -// such as pth (which defaults to wrapping ucontext on unix targets) -// -//Use this library only as a *last resort* - -struct cothread_struct { - ucontext_t cohandle; - void (*coentry)(); -}; - -cothread_t __co_active = 0, __co_primary = 0; -void co_entrypoint(cothread_t cothread); -void co_init(); - -/***** - * library functions - *****/ - -cothread_t co_active() { - if(__co_primary == 0)co_init(); - return __co_active; -} - -cothread_t co_create(unsigned int heapsize, void (*coentry)()) { - if(__co_primary == 0)co_init(); -cothread_struct *thread = (cothread_struct*)malloc(sizeof(cothread_struct)); - thread->coentry = coentry; - getcontext(&thread->cohandle); - heapsize += 512; - thread->cohandle.uc_stack.ss_sp = (char*)malloc(heapsize); - thread->cohandle.uc_stack.ss_size = heapsize; - makecontext(&thread->cohandle, (void (*)())co_entrypoint, 1, thread); - return (cothread_t)thread; -} - -void co_delete(cothread_t cothread) { -cothread_struct *thread = (cothread_struct*)cothread; - free(thread->cohandle.uc_stack.ss_sp); - free(thread); -} - -void co_switch(cothread_t cothread) { -cothread_struct *active = (cothread_struct*)__co_active; -cothread_struct *swap = (cothread_struct*)cothread; - __co_active = cothread; - swapcontext(&active->cohandle, &swap->cohandle); -} - -/***** - * internal functions - *****/ - -void co_entrypoint(cothread_t cothread) { - ((cothread_struct*)cothread)->coentry(); -} - -void co_init() { -cothread_struct *thread = (cothread_struct*)malloc(sizeof(cothread_struct)); - thread->coentry = 0; - getcontext(&thread->cohandle); - __co_active = __co_primary = (cothread_t)thread; -} diff --git a/libco.win.cpp b/libco.win.cpp deleted file mode 100644 index f0d16626..00000000 --- a/libco.win.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/* - libco.win (2007-09-08) - author: byuu - license: public domain -*/ - -#define WINVER 0x0400 -#define _WIN32_WINNT 0x0400 -#include -#include "../libco.h" - -struct cothread_struct { - void *cohandle; - void (*coentry)(); -}; - -cothread_t __co_active = 0, __co_primary = 0; - -void __stdcall co_entryproc(void*); -cothread_t co_init(); - -/***** - * library functions - *****/ - -cothread_t co_active() { - if(__co_primary == 0)co_init(); - return __co_active; -} - -cothread_t co_create(unsigned int heapsize, void (*coentry)()) { - if(__co_primary == 0)co_init(); -cothread_struct *s = (cothread_struct*)malloc(sizeof(cothread_struct)); - s->coentry = coentry; - s->cohandle = CreateFiber(heapsize + 512, co_entryproc, (void*)s); - return (cothread_t)s; -} - -void co_delete(cothread_t cothread) { -cothread_struct *s = (cothread_struct*)cothread; - DeleteFiber(s->cohandle); - free(cothread); -} - -void co_switch(cothread_t cothread) { - __co_active = cothread; -cothread_struct *s = (cothread_struct*)cothread; - SwitchToFiber(s->cohandle); -} - -/***** - * internal functions - *****/ - -void __stdcall co_entryproc(void *cothread) { - ((cothread_struct*)cothread)->coentry(); -} - -cothread_t co_init() { - ConvertThreadToFiber(0); -cothread_struct *s = (cothread_struct*)malloc(sizeof(cothread_struct)); - s->coentry = 0; - s->cohandle = GetCurrentFiber(); - __co_active = __co_primary = (cothread_t)s; - return __co_active; -} diff --git a/libco.x86-64.asm b/libco.x86-64.asm deleted file mode 100644 index 051987f0..00000000 --- a/libco.x86-64.asm +++ /dev/null @@ -1,146 +0,0 @@ -;***** -;libco.x86-64 (2007-12-11) -;author: byuu -;license: public domain -; -;cross-platform x86-64 implementation of libco -;thanks to Aaron Giles and Joel Yliluoma for various optimizations -;thanks to Lucas Newman and Vas Crabb for assistance with OS X support -; -;[ABI compatibility] -;- SystemV ( http://refspecs.freestandards.org/elf/x86_64-SysV-psABI.pdf ) -;- gcc; mac os x; x86-64 -;- gcc; linux; x86-64 -;- gcc; freebsd; x86-64 -; -;[nonvolatile registers] -;- rsp, rbp, rbx, r12, r13, r14, r15 -; -;[volatile registers] -;- rax, rcx, rdx, r8, r9, r10, r11, rdi, rsi -;- st0 - st7 -;- xmm0 - xmm15 -;***** - -;***** -;linker-specific name decorations -;***** - -%ifdef OSX -%define malloc _malloc -%define free _free - -%define co_active _co_active -%define co_create _co_create -%define co_delete _co_delete -%define co_switch _co_switch -%endif - -bits 64 - -section .bss - -align 8 -co_primary_buffer resb 512 - -section .data - -align 8 -co_active_context dq co_primary_buffer - -section .text - -extern malloc -extern free - -global co_active -global co_create -global co_delete -global co_switch - -;***** -;extern "C" cothread_t co_active(); -;return = rax -;***** - -align 16 -co_active: - mov rax,[co_active_context wrt rip] - ret - -;***** -;extern "C" cothread_t co_create(unsigned int heapsize, void (*coentry)()); -;rdi = heapsize -;rsi = coentry -;return = rax -;***** - -align 16 -co_create: -;create heap space (stack + context) - add rdi,512 ;allocate extra memory for contextual info - - push rdi ;backup volatile registers before malloc call - push rsi - - sub rsp,8 ;SSE 16-byte stack alignment - call malloc ;rax = malloc(rdi) - add rsp,8 - - pop rsi ;restore volatile registers - pop rdi - - add rdi,rax ;set rdi to point to top of stack heap - and rdi,-16 ;force 16-byte alignment of stack heap - -;store thread entry point + registers, so that first call to co_switch will execute coentry - mov qword[rdi-8],0 ;crash if entry point returns - mov qword[rdi-16],rsi ;entry point - mov qword[rdi-24],0 ;r15 - mov qword[rdi-32],0 ;r14 - mov qword[rdi-40],0 ;r13 - mov qword[rdi-48],0 ;r12 - mov qword[rdi-56],0 ;rbx - mov qword[rdi-64],0 ;rbp - sub rdi,64 - -;initialize context memory heap and return - mov [rax],rdi ;*cothread_t = stack heap pointer (rsp) - ret ;return allocated memory block as thread handle - -;***** -;extern "C" void co_delete(cothread_t cothread); -;rdi = cothread -;***** - -align 16 -co_delete: - jmp free ;free(rdi) - -;***** -;extern "C" void co_switch(cothread_t cothread); -;rdi = cothread -;***** - -align 16 -co_switch: - mov rax,[co_active_context wrt rip] ;backup current context - mov [co_active_context wrt rip],rdi ;set new active context - - push rbp - push rbx - push r12 - push r13 - push r14 - push r15 - mov [rax],rsp - - mov rsp,[rdi] - pop r15 - pop r14 - pop r13 - pop r12 - pop rbx - pop rbp - - ret diff --git a/libco.x86.asm b/libco.x86.asm deleted file mode 100644 index f151c4a8..00000000 --- a/libco.x86.asm +++ /dev/null @@ -1,155 +0,0 @@ -;***** -;libco.x86 (2007-12-11) -;author: byuu -;license: public domain -; -;cross-platform x86 implementation of libco -;thanks to Aaron Giles and Joel Yliluoma for various optimizations -;thanks to Lucas Newman and Vas Crabb for assistance with OS X support -; -;[ABI compatibility] -;- visual c++; windows; x86 -;- mingw; windows; x86 -;- gcc; mac os x; x86 -;- gcc; linux; x86 -;- gcc; freebsd; x86 -; -;[nonvolatile registers] -;- esp, ebp, edi, esi, ebx -; -;[volatile registers] -;- eax, ecx, edx -;- st0 - st7 -;- xmm0 - xmm15 -;***** - -;***** -;linker-specific name decorations -;***** - -%ifdef WIN -%define malloc _malloc -%define free _free - -%define co_active @co_active@0 -%define co_create @co_create@8 -%define co_delete @co_delete@4 -%define co_switch @co_switch@4 -%endif - -%ifdef OSX -%define malloc _malloc -%define free _free - -%define co_active _co_active -%define co_create _co_create -%define co_delete _co_delete -%define co_switch _co_switch -%endif - -bits 32 - -section .bss - -align 4 -co_primary_buffer resb 512 - -section .data - -align 4 -co_active_context dd co_primary_buffer - -section .text - -extern malloc -extern free - -global co_active -global co_create -global co_delete -global co_switch - -;***** -;extern "C" cothread_t fastcall co_active(); -;return = eax -;***** - -align 16 -co_active: - mov eax,[co_active_context] - ret - -;***** -;extern "C" cothread_t fastcall co_create(unsigned int heapsize, void (*coentry)()); -;ecx = heapsize -;edx = coentry -;return = eax -;***** - -align 16 -co_create: -;create heap space (stack + context) - add ecx,512 ;allocate extra memory for contextual info - - push ecx ;backup volatile registers before malloc call - push edx - - push ecx - call malloc ;eax = malloc(ecx) - add esp,4 - - pop edx ;restore volatile registers - pop ecx - - add ecx,eax ;set edx to point to top of stack heap - and ecx,-16 ;force 16-byte alignment of stack heap - -;store thread entry point + registers, so that first call to co_switch will execute coentry - mov dword[ecx-4],0 ;crash if entry point returns - mov dword[ecx-8],edx ;entry point - mov dword[ecx-12],0 ;ebp - mov dword[ecx-16],0 ;esi - mov dword[ecx-20],0 ;edi - mov dword[ecx-24],0 ;ebx - sub ecx,24 - -;initialize context memory heap and return - mov [eax],ecx ;*cothread_t = stack heap pointer (esp) - ret ;return allocated memory block as thread handle - -;***** -;extern "C" void fastcall co_delete(cothread_t cothread); -;ecx = cothread -;***** - -align 16 -co_delete: - sub esp,8 ;SSE 16-byte stack alignment - push ecx - call free ;free(ecx) - add esp,4+8 - ret - -;***** -;extern "C" void fastcall co_switch(cothread_t cothread); -;ecx = cothread -;***** - -align 16 -co_switch: - mov eax,[co_active_context] ;backup current context - mov [co_active_context],ecx ;set new active context - - push ebp - push esi - push edi - push ebx - mov [eax],esp - - mov esp,[ecx] - pop ebx - pop edi - pop esi - pop ebp - - ret diff --git a/libco.ppc.s b/ppc.s similarity index 100% rename from libco.ppc.s rename to ppc.s diff --git a/libco.ppc64.s b/ppc64.s similarity index 100% rename from libco.ppc64.s rename to ppc64.s diff --git a/sjlj.c b/sjlj.c new file mode 100644 index 00000000..86b2ea26 --- /dev/null +++ b/sjlj.c @@ -0,0 +1,102 @@ +/* + libco.sjlj (2008-01-28) + author: Nach + license: public domain +*/ + +/* + * Note this was designed for UNIX systems. Based on ideas expressed in a paper + * by Ralf Engelschall. + * For SJLJ on other systems, one would want to rewrite springboard() and + * co_create() and hack the jmb_buf stack pointer. + */ + +#define LIBCO_C +#include "../libco.h" +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + sigjmp_buf context; + void (*coentry)(void); + void *stack; +} cothread_struct; + +static thread_local cothread_struct co_primary; +static thread_local cothread_struct *creating, *co_running = 0; + +static void springboard(int ignored) { + if(sigsetjmp(creating->context, 0)) { + co_running->coentry(); + } +} + +cothread_t co_active() { + if(!co_running) co_running = &co_primary; + return (cothread_t)co_running; +} + +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)); + 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 = malloc(size); + 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; +} + +void co_delete(cothread_t cothread) { + if(cothread) { + if(((cothread_struct*)cothread)->stack) { + free(((cothread_struct*)cothread)->stack); + } + free(cothread); + } +} + +void co_switch(cothread_t cothread) { + if(!sigsetjmp(co_running->context, 0)) { + co_running = (cothread_struct*)cothread; + siglongjmp(co_running->context, 1); + } +} + +#ifdef __cplusplus +} +#endif diff --git a/ucontext.c b/ucontext.c new file mode 100644 index 00000000..47766e64 --- /dev/null +++ b/ucontext.c @@ -0,0 +1,67 @@ +/* + libco.ucontext (2008-01-28) + author: Nach + license: public domain +*/ + +/* + * WARNING: the overhead of POSIX ucontext is very high, + * assembly versions of libco or libco_sjlj should be much faster + * + * This library only exists for two reasons: + * 1 - as an initial test for the viability of a ucontext implementation + * 2 - to demonstrate the power and speed of libco over existing implementations, + * such as pth (which defaults to wrapping ucontext on unix targets) + * + * Use this library only as a *last resort* + */ + +#define LIBCO_C +#include "../libco.h" +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +static thread_local ucontext_t co_primary; +static thread_local ucontext_t *co_running = 0; + +cothread_t co_active() { + if(!co_running) co_running = &co_primary; + return (cothread_t)co_running; +} + +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)); + if(thread) { + if((!getcontext(thread) && !(thread->uc_stack.ss_sp = 0)) && (thread->uc_stack.ss_sp = malloc(heapsize))) { + thread->uc_link = co_running; + thread->uc_stack.ss_size = heapsize; + makecontext(thread, coentry, 0); + } else { + co_delete((cothread_t)thread); + thread = 0; + } + } + return (cothread_t)thread; +} + +void co_delete(cothread_t cothread) { + if(cothread) { + if(((ucontext_t*)cothread)->uc_stack.ss_sp) { free(((ucontext_t*)cothread)->uc_stack.ss_sp); } + free(cothread); + } +} + +void co_switch(cothread_t cothread) { + ucontext_t *old_thread = co_running; + co_running = (ucontext_t*)cothread; + swapcontext(old_thread, co_running); +} + +#ifdef __cplusplus +} +#endif diff --git a/x86-64.c b/x86-64.c new file mode 100644 index 00000000..e1e8c7f3 --- /dev/null +++ b/x86-64.c @@ -0,0 +1,81 @@ +/* + libco.x86-64 (2008-01-28) + author: byuu + license: public domain +*/ + +#define LIBCO_C +#include "../libco.h" +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +static thread_local long co_active_buffer[32]; +static thread_local cothread_t co_active_ = 0; + +static void crash() { + assert(0); /* called only if cothread_t entrypoint returns */ +} + +cothread_t co_active() { + if(!co_active_) co_active_ = &co_active_buffer; + return co_active_; +} + +cothread_t co_create(unsigned int size, void (*entrypoint)(void)) { + cothread_t handle; + assert(sizeof(long) == 8); + if(!co_active_) co_active_ = &co_active_buffer; + size += 128; /* allocate additional space for storage */ + size &= ~15; /* align stack to 16-byte boundary */ + + if(handle = (cothread_t)calloc(size, 1)) { + 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; +} + +void co_delete(cothread_t handle) { + free(handle); +} + +void co_switch(cothread_t to) { + register long stack = *(long*)to; /* stack[0] = "to" thread entry point */ + register cothread_t from = co_active_; + co_active_ = to; + + __asm__ __volatile__( + "movq %%rsp,(%1) \n\t" /* save old stack pointer */ + "movq (%0),%%rsp \n\t" /* load new stack pointer */ + "addq $8,%%rsp \n\t" /* "pop" return address off stack */ + + "movq %%rbp, 8(%1) \n\t" /* backup non-volatile registers */ + "movq %%rbx,16(%1) \n\t" + "movq %%r12,24(%1) \n\t" + "movq %%r13,32(%1) \n\t" + "movq %%r14,40(%1) \n\t" + "movq %%r15,48(%1) \n\t" + + "movq 8(%0),%%rbp \n\t" /* restore non-volatile registers */ + "movq 16(%0),%%rbx \n\t" + "movq 24(%0),%%r12 \n\t" + "movq 32(%0),%%r13 \n\t" + "movq 40(%0),%%r14 \n\t" + "movq 48(%0),%%r15 \n\t" + + "jmp *(%2) \n\t" /* jump into "to" thread */ + : /* no outputs */ + : "r" (to), "r" (from), "r" (stack) + ); +} + +#ifdef __cplusplus +} +#endif diff --git a/x86.c b/x86.c new file mode 100644 index 00000000..18af8ac4 --- /dev/null +++ b/x86.c @@ -0,0 +1,110 @@ +/* + libco.x86 (2008-01-28) + author: byuu + license: public domain +*/ + +#define LIBCO_C +#include "../libco.h" +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +static thread_local long co_active_buffer[32]; +static thread_local cothread_t co_active_ = 0; + +static void crash() { + assert(0); /* called only if cothread_t entrypoint returns */ +} + +cothread_t co_active() { + if(!co_active_) co_active_ = &co_active_buffer; + return co_active_; +} + +cothread_t co_create(unsigned int size, void (*entrypoint)(void)) { + cothread_t handle; + assert(sizeof(long) == 4); + if(!co_active_) co_active_ = &co_active_buffer; + size += 128; /* allocate additional space for storage */ + size &= ~15; /* align stack to 16-byte boundary */ + + if(handle = (cothread_t)calloc(size, 1)) { + 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; +} + +void co_delete(cothread_t handle) { + free(handle); +} + +#if defined(__GNUC__) + +void co_switch(cothread_t to) { + register long stack = *(long*)to; /* stack[0] = "to" thread entry point */ + register cothread_t from = co_active_; + co_active_ = to; + + __asm__ __volatile__( + "movl %%esp,(%1) \n\t" /* save old stack pointer */ + "movl (%0),%%esp \n\t" /* load new stack pointer */ + "addl $4,%%esp \n\t" /* "pop" return address off stack */ + + "movl %%ebp, 4(%1) \n\t" /* backup non-volatile registers */ + "movl %%esi, 8(%1) \n\t" + "movl %%edi,12(%1) \n\t" + "movl %%ebx,16(%1) \n\t" + + "movl 4(%0),%%ebp \n\t" /* restore non-volatile registers */ + "movl 8(%0),%%esi \n\t" + "movl 12(%0),%%edi \n\t" + "movl 16(%0),%%ebx \n\t" + + "jmp *(%2) \n\t" /* jump into "to" thread */ + : /* no outputs */ + : "r" (to), "r" (from), "r" (stack) + ); +} + +#elif defined(_MSC_VER) + +__declspec(naked) __declspec(noinline) +static void __fastcall co_swap(register cothread_t to, register cothread_t from) { + /* ecx = to, edx = from */ + __asm { + mov [edx],esp + mov esp,[ecx] + pop eax + + mov [edx+ 4],ebp + mov [edx+ 8],esi + mov [edx+12],edi + mov [edx+16],ebx + + mov ebp,[ecx+ 4] + mov esi,[ecx+ 8] + mov edi,[ecx+12] + mov ebx,[ecx+16] + + jmp eax + } +} + +void co_switch(cothread_t handle) { + register cothread_t co_prev_ = co_active_; + co_swap(co_active_ = handle, co_prev_); +} + +#endif + +#ifdef __cplusplus +} +#endif