├── -test ├── cc.bat ├── cc.sh ├── test.h ├── test_args.cpp ├── test_timing └── test_timing.cpp ├── LICENSE ├── README.md ├── amd64.c ├── arm.c ├── doc ├── style.css ├── targets.html └── usage.html ├── fiber.c ├── libco.c ├── libco.h ├── ppc.c ├── sjlj.c ├── ucontext.c └── x86.c /-test/cc.bat: -------------------------------------------------------------------------------- 1 | gcc -O3 -fomit-frame-pointer -o libco.o -c ../libco.c 2 | g++ -O3 -fomit-frame-pointer -c test_timing.cpp 3 | g++ -O3 -fomit-frame-pointer -o test_timing libco.o test_timing.o 4 | @del *.o 5 | -------------------------------------------------------------------------------- /-test/cc.sh: -------------------------------------------------------------------------------- 1 | g++49 -O3 -fomit-frame-pointer -c test_timing.cpp 2 | gcc49 -O3 -fomit-frame-pointer -o libco.o -c ../libco.c 3 | g++49 -O3 -fomit-frame-pointer -o test_timing libco.o test_timing.o 4 | rm -f *.o 5 | -------------------------------------------------------------------------------- /-test/test.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "../libco.h" 7 | -------------------------------------------------------------------------------- /-test/test_args.cpp: -------------------------------------------------------------------------------- 1 | /***** 2 | * cothread parameterized function example 3 | ***** 4 | * entry point to cothreads cannot take arguments. 5 | * this is due to portability issues: each processor, 6 | * operating system, programming language and compiler 7 | * can use different parameter passing methods, so 8 | * arguments to the cothread entry points were omitted. 9 | * 10 | * however, the behavior can easily be simulated by use 11 | * of a specialized co_switch to set global parameters to 12 | * be used as function arguments. 13 | * 14 | * in this way, with a bit of extra red tape, one gains 15 | * even more flexibility than would be possible with a 16 | * fixed argument list entry point, such as void (*)(void*), 17 | * as any number of arguments can be used. 18 | * 19 | * this also eliminates race conditions where a pointer 20 | * passed to co_create may have changed or become invalidated 21 | * before call to co_switch, as said pointer would now be set 22 | * when calling co_switch, instead. 23 | *****/ 24 | 25 | #include "test.h" 26 | 27 | cothread_t thread[3]; 28 | 29 | namespace co_arg { 30 | int param_x; 31 | int param_y; 32 | }; 33 | 34 | //one could also call this co_init or somesuch if they preferred ... 35 | void co_switch(cothread_t thread, int param_x, int param_y) { 36 | co_arg::param_x = param_x; 37 | co_arg::param_y = param_y; 38 | co_switch(thread); 39 | } 40 | 41 | void co_entrypoint() { 42 | int param_x = co_arg::param_x; 43 | int param_y = co_arg::param_y; 44 | printf("co_entrypoint(%d, %d)\n", param_x, param_y); 45 | co_switch(thread[0]); 46 | 47 | //co_arg::param_x will change here (due to co_switch(cothread_t, int, int) call changing values), 48 | //however, param_x and param_y will persist as they are thread local 49 | 50 | printf("co_entrypoint(%d, %d)\n", param_x, param_y); 51 | co_switch(thread[0]); 52 | throw; 53 | } 54 | 55 | int main() { 56 | printf("cothread parameterized function example\n\n"); 57 | 58 | thread[0] = co_active(); 59 | thread[1] = co_create(65536, co_entrypoint); 60 | thread[2] = co_create(65536, co_entrypoint); 61 | 62 | //use specialized co_switch(cothread_t, int, int) for initial co_switch call 63 | co_switch(thread[1], 1, 2); 64 | co_switch(thread[2], 4, 8); 65 | 66 | //after first call, entry point arguments have been initialized, standard 67 | //co_switch(cothread_t) can be used from now on 68 | co_switch(thread[2]); 69 | co_switch(thread[1]); 70 | 71 | printf("\ndone\n"); 72 | #if defined(_MSC_VER) || defined(__DJGPP__) 73 | getch(); 74 | #endif 75 | return 0; 76 | } 77 | -------------------------------------------------------------------------------- /-test/test_timing: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/creationix/libco/50d7167fdb56a3bf905eafcdad2232075a1ff6c4/-test/test_timing -------------------------------------------------------------------------------- /-test/test_timing.cpp: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | enum { Iterations = 500000000 }; 3 | 4 | namespace thread { 5 | cothread_t x; 6 | cothread_t y; 7 | volatile int counter; 8 | } 9 | 10 | void co_timingtest() { 11 | for(;;) { 12 | thread::counter++; 13 | co_switch(thread::x); 14 | } 15 | } 16 | 17 | void sub_timingtest() { 18 | thread::counter++; 19 | } 20 | 21 | int main() { 22 | printf("context-switching timing test\n\n"); 23 | time_t start, end; 24 | int i, t1, t2; 25 | 26 | start = clock(); 27 | for(thread::counter = 0, i = 0; i < Iterations; i++) { 28 | sub_timingtest(); 29 | } 30 | end = clock(); 31 | 32 | t1 = (int)difftime(end, start); 33 | printf("%2.3f seconds per 50 million subroutine calls (%d iterations)\n", (float)t1 / CLOCKS_PER_SEC, thread::counter); 34 | 35 | thread::x = co_active(); 36 | thread::y = co_create(65536, co_timingtest); 37 | 38 | start = clock(); 39 | for(thread::counter = 0, i = 0; i < Iterations; i++) { 40 | co_switch(thread::y); 41 | } 42 | end = clock(); 43 | 44 | co_delete(thread::y); 45 | 46 | t2 = (int)difftime(end, start); 47 | printf("%2.3f seconds per 100 million co_switch calls (%d iterations)\n", (float)t2 / CLOCKS_PER_SEC, thread::counter); 48 | 49 | printf("co_switch skew = %fx\n\n", (double)t2 / (double)t1); 50 | return 0; 51 | } 52 | 53 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | libco is released to the public domain. 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # libco 2 | 3 | Mirror of http://byuu.org/library/libco/ 4 | 5 | 6 | **License:** 7 | libco is released to the public domain. 8 | 9 | ------------------------------------------------------------------------ 10 | 11 | **Contact:** 12 | At present, you may contact me at setsunakun0 at hotmail dot com. 13 | I am interested in knowing of any projects that make use of this library, though this is only a courtesy. 14 | 15 | ------------------------------------------------------------------------ 16 | 17 | **Foreword:** 18 | libco is a cross-platform, public domain implementation of cooperative-multithreading; a feature that is sorely lacking from the ISO C/C++ standard. 19 | The library is designed for maximum speed and portability, and not for safety or features. If safety or extra functionality is desired, a wrapper API can easily be written to encapsulate all library functions. 20 | Behavior of executing operations that are listed as not permitted below result in undefined behavior. They may work anyway, they may cause undesired / unknown behavior, or they may crash the program entirely. 21 | The goal of this library was to simplify the base API as much as possible, implementing only that which cannot be implemented using pure C. Additional functionality after this would only complicate ports of this library to new platforms. 22 | 23 | ------------------------------------------------------------------------ 24 | 25 | **Porting:** 26 | This document is included as a reference for porting libco. Please submit any ports you create to me, so that libco can become more useful. Please note that since libco is public domain, you must submit your code as a work of the public domain in order for it to be included in the official distribution. Full credit will be given in the source code of the official release. Please do not bother submitting code to me under any other license – including GPL, LGPL, BSD or CC – I am not interested in creating a library with multiple different licenses depending on which targets are used. 27 | 28 | ------------------------------------------------------------------------ 29 | 30 | **Synopsis:** 31 | ```c 32 | typedef void* cothread_t; 33 | cothread_t co_active(); 34 | cothread_t co_create(unsigned int heapsize, void (*coentry)(void)); 35 | void co_delete(cothread_t cothread); 36 | void co_switch(cothread_t cothread); 37 | ``` 38 | 39 | ------------------------------------------------------------------------ 40 | 41 | **Usage:** 42 | 43 | ------------------------------------------------------------------------ 44 | 45 | ```c 46 | typedef void* cothread_t; 47 | ``` 48 | 49 | Handle to cothread. 50 | Handle must be of type void\*. 51 | A value of null (0) indicates an uninitialized or invalid handle, whereas a non-zero value indicates a valid handle. 52 | 53 | ------------------------------------------------------------------------ 54 | 55 | ```c 56 | cothread_t co_active(); 57 | ``` 58 | 59 | Return handle to current cothread. Always returns a valid handle, even when called from the main program thread. 60 | 61 | ------------------------------------------------------------------------ 62 | 63 | ```c 64 | cothread_t co_create(unsigned int heapsize, void (*coentry)(void)); 65 | ``` 66 | 67 | Create new cothread. 68 | Heapsize is the amount of memory allocated for the cothread stack, specified in bytes. This is unfortunately impossible to make fully portable. It is recommended to specify sizes using \`n \* sizeof(void\*)’. It is better to err on the side of caution and allocate more memory than will be needed to ensure compatibility with other platforms, within reason. 69 | 70 | --------------------------------------------------------------------- 71 | **Supported targets:** 72 | Note that supported targets are only those that have been tested and confirmed working. It is quite possible that libco will work on more processors, compilers and operating systems than those listed below. 73 | 74 | ------------------------------------------------------------------------ 75 | 76 | **libco.x86** 77 | Overhead: ~5x 78 | Supported processor(s): 32-bit x86 79 | Supported compiler(s): any 80 | Supported operating system(s): 81 | 82 | - Windows 83 | - Mac OS X 84 | - Linux 85 | - BSD 86 | 87 | ------------------------------------------------------------------------ 88 | 89 | **libco.amd64** 90 | Overhead: ~10x (Windows), ~6x (all other platforms) 91 | Supported processor(s): 64-bit amd64 92 | Supported compiler(s): any 93 | Supported operating system(s): 94 | 95 | - Windows 96 | - Mac OS X 97 | - Linux 98 | - BSD 99 | 100 | ------------------------------------------------------------------------ 101 | 102 | **libco.ppc** 103 | Overhead: ~20x 104 | Supported processor(s): 32-bit PowerPC, 64-bit PowerPC 105 | Supported compiler(s): GNU GCC 106 | Supported operating system(s): 107 | 108 |
  • 109 | Mac OS X 110 | 111 |
  • 112 |
  • 113 | Linux 114 | 115 |
  • 116 |
  • 117 | BSD 118 | 119 |
  • 120 |
  • 121 | Playstation 3 122 | 123 |
  • 124 | 125 | Note: this module contains compiler flags to enable/disable FPU and Altivec support. 126 | 127 | ------------------------------------------------------------------------ 128 | 129 | **libco.fiber** 130 | Overhead: ~15x 131 | Supported processor(s): Processor independent 132 | Supported compiler(s): any 133 | Supported operating system(s): 134 | 135 | - Windows 136 | 137 | ------------------------------------------------------------------------ 138 | 139 | **libco.sjlj** 140 | Overhead: ~30x 141 | Supported processor(s): Processor independent 142 | Supported compiler(s): any 143 | Supported operating system(s): 144 | 145 | - Mac OS X 146 | - Linux 147 | - BSD 148 | - Solaris 149 | 150 | ------------------------------------------------------------------------ 151 | 152 | **libco.ucontext** 153 | Overhead: **~300x** 154 | Supported processor(s): Processor independent 155 | Supported compiler(s): any 156 | Supported operating system(s): 157 | 158 | - Linux 159 | - BSD 160 | 161 | ------------------------------------------------------------------------ 162 | 163 | © 2013–2015 John MacFarlane 164 | -------------------------------------------------------------------------------- /amd64.c: -------------------------------------------------------------------------------- 1 | /* 2 | libco.amd64 (2015-06-19) 3 | author: byuu 4 | license: public domain 5 | */ 6 | 7 | #define LIBCO_C 8 | #include "libco.h" 9 | 10 | //#define LIBCO_AMD64_NO_FPU 11 | 12 | #include 13 | #include 14 | 15 | #ifdef __cplusplus 16 | extern "C" { 17 | #endif 18 | 19 | static thread_local long long co_active_buffer[64]; 20 | static thread_local cothread_t co_active_handle = 0; 21 | static void (*co_swap)(cothread_t, cothread_t) = 0; 22 | 23 | #ifdef _WIN32 24 | /* ABI: Win64 */ 25 | static unsigned char co_swap_function[] = { 26 | 0x48, 0x89, 0x22, /* mov [rdx],rsp */ 27 | 0x48, 0x8b, 0x21, /* mov rsp,[rcx] */ 28 | 0x58, /* pop rax */ 29 | 0x48, 0x89, 0x6a, 0x08, /* mov [rdx+ 8],rbp */ 30 | 0x48, 0x89, 0x72, 0x10, /* mov [rdx+16],rsi */ 31 | 0x48, 0x89, 0x7a, 0x18, /* mov [rdx+24],rdi */ 32 | 0x48, 0x89, 0x5a, 0x20, /* mov [rdx+32],rbx */ 33 | 0x4c, 0x89, 0x62, 0x28, /* mov [rdx+40],r12 */ 34 | 0x4c, 0x89, 0x6a, 0x30, /* mov [rdx+48],r13 */ 35 | 0x4c, 0x89, 0x72, 0x38, /* mov [rdx+56],r14 */ 36 | 0x4c, 0x89, 0x7a, 0x40, /* mov [rdx+64],r15 */ 37 | #if !defined(LIBCO_AMD64_NO_FPU) 38 | 0x0f, 0x29, 0x72, 0x50, /* movaps [rdx+ 80],xmm6 */ 39 | 0x0f, 0x29, 0x7a, 0x60, /* movaps [rdx+ 96],xmm7 */ 40 | 0x44, 0x0f, 0x29, 0x42, 0x70, /* movaps [rdx+112],xmm8 */ 41 | 0x48, 0x83, 0xc2, 0x70, /* add rdx,112 */ 42 | 0x44, 0x0f, 0x29, 0x4a, 0x10, /* movaps [rdx+ 16],xmm9 */ 43 | 0x44, 0x0f, 0x29, 0x52, 0x20, /* movaps [rdx+ 32],xmm10 */ 44 | 0x44, 0x0f, 0x29, 0x5a, 0x30, /* movaps [rdx+ 48],xmm11 */ 45 | 0x44, 0x0f, 0x29, 0x62, 0x40, /* movaps [rdx+ 64],xmm12 */ 46 | 0x44, 0x0f, 0x29, 0x6a, 0x50, /* movaps [rdx+ 80],xmm13 */ 47 | 0x44, 0x0f, 0x29, 0x72, 0x60, /* movaps [rdx+ 96],xmm14 */ 48 | 0x44, 0x0f, 0x29, 0x7a, 0x70, /* movaps [rdx+112],xmm15 */ 49 | #endif 50 | 0x48, 0x8b, 0x69, 0x08, /* mov rbp,[rcx+ 8] */ 51 | 0x48, 0x8b, 0x71, 0x10, /* mov rsi,[rcx+16] */ 52 | 0x48, 0x8b, 0x79, 0x18, /* mov rdi,[rcx+24] */ 53 | 0x48, 0x8b, 0x59, 0x20, /* mov rbx,[rcx+32] */ 54 | 0x4c, 0x8b, 0x61, 0x28, /* mov r12,[rcx+40] */ 55 | 0x4c, 0x8b, 0x69, 0x30, /* mov r13,[rcx+48] */ 56 | 0x4c, 0x8b, 0x71, 0x38, /* mov r14,[rcx+56] */ 57 | 0x4c, 0x8b, 0x79, 0x40, /* mov r15,[rcx+64] */ 58 | #if !defined(LIBCO_AMD64_NO_FPU) 59 | 0x0f, 0x28, 0x71, 0x50, /* movaps xmm6, [rcx+ 80] */ 60 | 0x0f, 0x28, 0x79, 0x60, /* movaps xmm7, [rcx+ 96] */ 61 | 0x44, 0x0f, 0x28, 0x41, 0x70, /* movaps xmm8, [rcx+112] */ 62 | 0x48, 0x83, 0xc1, 0x70, /* add rcx,112 */ 63 | 0x44, 0x0f, 0x28, 0x49, 0x10, /* movaps xmm9, [rcx+ 16] */ 64 | 0x44, 0x0f, 0x28, 0x51, 0x20, /* movaps xmm10,[rcx+ 32] */ 65 | 0x44, 0x0f, 0x28, 0x59, 0x30, /* movaps xmm11,[rcx+ 48] */ 66 | 0x44, 0x0f, 0x28, 0x61, 0x40, /* movaps xmm12,[rcx+ 64] */ 67 | 0x44, 0x0f, 0x28, 0x69, 0x50, /* movaps xmm13,[rcx+ 80] */ 68 | 0x44, 0x0f, 0x28, 0x71, 0x60, /* movaps xmm14,[rcx+ 96] */ 69 | 0x44, 0x0f, 0x28, 0x79, 0x70, /* movaps xmm15,[rcx+112] */ 70 | #endif 71 | 0xff, 0xe0, /* jmp rax */ 72 | }; 73 | 74 | #include 75 | 76 | void co_init() { 77 | DWORD old_privileges; 78 | VirtualProtect(co_swap_function, sizeof co_swap_function, PAGE_EXECUTE_READWRITE, &old_privileges); 79 | } 80 | #else 81 | /* ABI: SystemV */ 82 | static unsigned char co_swap_function[] = { 83 | 0x48, 0x89, 0x26, /* mov [rsi],rsp */ 84 | 0x48, 0x8b, 0x27, /* mov rsp,[rdi] */ 85 | 0x58, /* pop rax */ 86 | 0x48, 0x89, 0x6e, 0x08, /* mov [rsi+ 8],rbp */ 87 | 0x48, 0x89, 0x5e, 0x10, /* mov [rsi+16],rbx */ 88 | 0x4c, 0x89, 0x66, 0x18, /* mov [rsi+24],r12 */ 89 | 0x4c, 0x89, 0x6e, 0x20, /* mov [rsi+32],r13 */ 90 | 0x4c, 0x89, 0x76, 0x28, /* mov [rsi+40],r14 */ 91 | 0x4c, 0x89, 0x7e, 0x30, /* mov [rsi+48],r15 */ 92 | 0x48, 0x8b, 0x6f, 0x08, /* mov rbp,[rdi+ 8] */ 93 | 0x48, 0x8b, 0x5f, 0x10, /* mov rbx,[rdi+16] */ 94 | 0x4c, 0x8b, 0x67, 0x18, /* mov r12,[rdi+24] */ 95 | 0x4c, 0x8b, 0x6f, 0x20, /* mov r13,[rdi+32] */ 96 | 0x4c, 0x8b, 0x77, 0x28, /* mov r14,[rdi+40] */ 97 | 0x4c, 0x8b, 0x7f, 0x30, /* mov r15,[rdi+48] */ 98 | 0xff, 0xe0, /* jmp rax */ 99 | }; 100 | 101 | #include 102 | #include 103 | 104 | void co_init() { 105 | unsigned long long addr = (unsigned long long)co_swap_function; 106 | unsigned long long base = addr - (addr % sysconf(_SC_PAGESIZE)); 107 | unsigned long long size = (addr - base) + sizeof co_swap_function; 108 | mprotect((void*)base, size, PROT_READ | PROT_WRITE | PROT_EXEC); 109 | } 110 | #endif 111 | 112 | static void crash() { 113 | assert(0); /* called only if cothread_t entrypoint returns */ 114 | } 115 | 116 | cothread_t co_active() { 117 | if(!co_active_handle) co_active_handle = &co_active_buffer; 118 | return co_active_handle; 119 | } 120 | 121 | cothread_t co_create(unsigned int size, void (*entrypoint)(void)) { 122 | cothread_t handle; 123 | if(!co_swap) { 124 | co_init(); 125 | co_swap = (void (*)(cothread_t, cothread_t))co_swap_function; 126 | } 127 | if(!co_active_handle) co_active_handle = &co_active_buffer; 128 | size += 512; /* allocate additional space for storage */ 129 | size &= ~15; /* align stack to 16-byte boundary */ 130 | 131 | if(handle = (cothread_t)malloc(size)) { 132 | long long *p = (long long*)((char*)handle + size); /* seek to top of stack */ 133 | *--p = (long long)crash; /* crash if entrypoint returns */ 134 | *--p = (long long)entrypoint; /* start of function */ 135 | *(long long*)handle = (long long)p; /* stack pointer */ 136 | } 137 | 138 | return handle; 139 | } 140 | 141 | void co_delete(cothread_t handle) { 142 | free(handle); 143 | } 144 | 145 | void co_switch(cothread_t handle) { 146 | register cothread_t co_previous_handle = co_active_handle; 147 | co_swap(co_active_handle = handle, co_previous_handle); 148 | } 149 | 150 | #ifdef __cplusplus 151 | } 152 | #endif 153 | -------------------------------------------------------------------------------- /arm.c: -------------------------------------------------------------------------------- 1 | /* 2 | libco.arm (2015-06-18) 3 | author: byuu 4 | license: public domain 5 | */ 6 | 7 | #define LIBCO_C 8 | #include "libco.h" 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #ifdef __cplusplus 16 | extern "C" { 17 | #endif 18 | 19 | static thread_local unsigned long co_active_buffer[64]; 20 | static thread_local cothread_t co_active_handle = 0; 21 | static void (*co_swap)(cothread_t, cothread_t) = 0; 22 | 23 | static unsigned long co_swap_function[] = { 24 | 0xe8a16ff0, /* stmia r1!, {r4-r11,sp,lr} */ 25 | 0xe8b0aff0, /* ldmia r0!, {r4-r11,sp,pc} */ 26 | 0xe12fff1e, /* bx lr */ 27 | }; 28 | 29 | void co_init() { 30 | unsigned long addr = (unsigned long)co_swap_function; 31 | unsigned long base = addr - (addr % sysconf(_SC_PAGESIZE)); 32 | unsigned long size = (addr - base) + sizeof co_swap_function; 33 | mprotect((void*)base, size, PROT_READ | PROT_WRITE | PROT_EXEC); 34 | } 35 | 36 | cothread_t co_active() { 37 | if(!co_active_handle) co_active_handle = &co_active_buffer; 38 | return co_active_handle; 39 | } 40 | 41 | cothread_t co_create(unsigned int size, void (*entrypoint)(void)) { 42 | unsigned long* handle = 0; 43 | if(!co_swap) { 44 | co_init(); 45 | co_swap = (void (*)(cothread_t, cothread_t))co_swap_function; 46 | } 47 | if(!co_active_handle) co_active_handle = &co_active_buffer; 48 | size += 256; 49 | size &= ~15; 50 | 51 | if(handle = (unsigned long*)malloc(size)) { 52 | unsigned long* p = (unsigned long*)((unsigned char*)handle + size); 53 | handle[8] = (unsigned long)p; 54 | handle[9] = (unsigned long)entrypoint; 55 | } 56 | 57 | return handle; 58 | } 59 | 60 | void co_delete(cothread_t handle) { 61 | free(handle); 62 | } 63 | 64 | void co_switch(cothread_t handle) { 65 | cothread_t co_previous_handle = co_active_handle; 66 | co_swap(co_active_handle = handle, co_previous_handle); 67 | } 68 | 69 | #ifdef __cplusplus 70 | } 71 | #endif 72 | -------------------------------------------------------------------------------- /doc/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: #333; 3 | color: #fff; 4 | } 5 | 6 | code { 7 | background: #444; 8 | } 9 | -------------------------------------------------------------------------------- /doc/targets.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Supported targets:

    9 | 10 | Note that supported targets are only those that have been tested and confirmed 11 | working. It is quite possible that libco will work on more processors, compilers 12 | and operating systems than those listed below. 13 |
    14 | 15 | libco.x86
    16 | Overhead: ~5x
    17 | Supported processor(s): 32-bit x86
    18 | Supported compiler(s): any
    19 | Supported operating system(s):
      20 |
    • Windows
    • 21 |
    • Mac OS X
    • 22 |
    • Linux
    • 23 |
    • BSD
    • 24 |
    25 |
    26 | 27 | libco.amd64
    28 | Overhead: ~10x (Windows), ~6x (all other platforms)
    29 | Supported processor(s): 64-bit amd64
    30 | Supported compiler(s): any
    31 | Supported operating system(s):
      32 |
    • Windows
    • 33 |
    • Mac OS X
    • 34 |
    • Linux
    • 35 |
    • BSD
    • 36 |
    37 |
    38 | 39 | libco.ppc
    40 | Overhead: ~20x
    41 | Supported processor(s): 32-bit PowerPC, 64-bit PowerPC
    42 | Supported compiler(s): GNU GCC
    43 | Supported operating system(s):
      44 |
    45 |
  • Mac OS X
  • 46 |
  • Linux
  • 47 |
  • BSD
  • 48 |
  • Playstation 3
  • 49 | 50 |
    51 | 52 | Note: this module contains compiler flags to enable/disable FPU and Altivec 53 | support. 54 | 55 |
    56 | 57 | libco.fiber
    58 | Overhead: ~15x
    59 | Supported processor(s): Processor independent
    60 | Supported compiler(s): any
    61 | Supported operating system(s):
      62 |
    • Windows
    • 63 |
    64 |
    65 | 66 | libco.sjlj
    67 | Overhead: ~30x
    68 | Supported processor(s): Processor independent
    69 | Supported compiler(s): any
    70 | Supported operating system(s):
      71 |
    • Mac OS X
    • 72 |
    • Linux
    • 73 |
    • BSD
    • 74 |
    • Solaris
    • 75 |
    76 |
    77 | 78 | libco.ucontext
    79 | Overhead: ~300x
    80 | Supported processor(s): Processor independent
    81 | Supported compiler(s): any
    82 | Supported operating system(s):
      83 |
    • Linux
    • 84 |
    • BSD
    • 85 |
    86 |
    87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /doc/usage.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | License:

    9 | libco is released to the public domain. 10 |
    11 | 12 | Contact:

    13 | At present, you may contact me at setsunakun0 at hotmail dot com.
    14 | I am interested in knowing of any projects that make use of this library, 15 | though this is only a courtesy. 16 |
    17 | 18 | Foreword:

    19 | libco is a cross-platform, public domain implementation of 20 | cooperative-multithreading; a feature that is sorely lacking 21 | from the ISO C/C++ standard.
    22 | The library is designed for maximum speed and portability, and 23 | not for safety or features. If safety or extra functionality is desired, 24 | a wrapper API can easily be written to encapsulate all library functions.
    25 | Behavior of executing operations that are listed as not permitted 26 | below result in undefined behavior. They may work anyway, they 27 | may cause undesired / unknown behavior, or they may crash the 28 | program entirely.
    29 | The goal of this library was to simplify the base API as much as possible, 30 | implementing only that which cannot be implemented using pure C. Additional 31 | functionality after this would only complicate ports of this library to new 32 | platforms. 33 |
    34 | 35 | Porting:

    36 | This document is included as a reference for porting libco. Please submit any 37 | ports you create to me, so that libco can become more useful. Please note that 38 | since libco is public domain, you must submit your code as a work of the 39 | public domain in order for it to be included in the official distribution. 40 | Full credit will be given in the source code of the official release. Please 41 | do not bother submitting code to me under any other license -- including GPL, 42 | LGPL, BSD or CC -- I am not interested in creating a library with multiple 43 | different licenses depending on which targets are used. 44 |
    45 | 46 | Synopsis:

    47 | 48 | typedef void* cothread_t;
    49 |
    50 | cothread_t co_active();
    51 | cothread_t co_create(unsigned int heapsize, void (*coentry)(void));
    52 | void       co_delete(cothread_t cothread);
    53 | void       co_switch(cothread_t cothread);
    54 |
    55 |
    56 | 57 | Usage: 58 |
    59 | 60 | typedef void* cothread_t;

    61 | Handle to cothread.
    62 | Handle must be of type void*.
    63 | A value of null (0) indicates an uninitialized or invalid 64 | handle, whereas a non-zero value indicates a valid handle. 65 |
    66 | 67 | cothread_t co_active();

    68 | Return handle to current cothread. Always returns a valid handle, even when 69 | called from the main program thread. 70 |
    71 | 72 | cothread_t co_create(unsigned int heapsize, void (*coentry)(void));

    73 | Create new cothread.
    74 | Heapsize is the amount of memory allocated for the cothread stack, specified 75 | in bytes. This is unfortunately impossible to make fully portable. It is 76 | recommended to specify sizes using `n * sizeof(void*)'. It is better to err 77 | on the side of caution and allocate more memory than will be needed to ensure 78 | compatibility with other platforms, within reason. A typical heapsize for a 79 | 32-bit architecture is ~1MB.
    80 | When the new cothread is first called, program execution jumps to coentry. 81 | This function does not take any arguments, due to portability issues with 82 | passing function arguments. However, arguments can be simulated by the use 83 | of global variables, which can be set before the first call to each cothread.
    84 | coentry() must not return, and should end with an appropriate co_switch() 85 | statement. Behavior is undefined if entry point returns normally.
    86 | Library is responsible for allocating cothread stack memory, to free 87 | the user from needing to allocate special memory capable of being used 88 | as program stack memory on platforms where this is required.
    89 | User is always responsible for deleting cothreads with co_delete().
    90 | Return value of null (0) indicates cothread creation failed. 91 |
    92 | 93 | void co_delete(cothread_t cothread);

    94 | Delete specified cothread.
    95 | Null (0) or invalid cothread handle is not allowed.
    96 | Passing handle of active cothread to this function is not allowed.
    97 | Passing handle of primary cothread is not allowed. 98 |
    99 | 100 | void co_switch(cothread_t cothread);

    101 | Switch to specified cothread.
    102 | Null (0) or invalid cothread handle is not allowed.
    103 | Passing handle of active cothread to this function is not allowed. 104 |
    105 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /fiber.c: -------------------------------------------------------------------------------- 1 | /* 2 | libco.win (2008-01-28) 3 | authors: Nach, byuu 4 | license: public domain 5 | */ 6 | 7 | #define LIBCO_C 8 | #include "libco.h" 9 | 10 | #define WINVER 0x0400 11 | #define _WIN32_WINNT 0x0400 12 | #include 13 | 14 | #ifdef __cplusplus 15 | extern "C" { 16 | #endif 17 | 18 | static thread_local cothread_t co_active_ = 0; 19 | 20 | static void __stdcall co_thunk(void *coentry) { 21 | ((void (*)(void))coentry)(); 22 | } 23 | 24 | cothread_t co_active() { 25 | if(!co_active_) { 26 | ConvertThreadToFiber(0); 27 | co_active_ = GetCurrentFiber(); 28 | } 29 | return co_active_; 30 | } 31 | 32 | cothread_t co_create(unsigned int heapsize, void (*coentry)(void)) { 33 | if(!co_active_) { 34 | ConvertThreadToFiber(0); 35 | co_active_ = GetCurrentFiber(); 36 | } 37 | return (cothread_t)CreateFiber(heapsize, co_thunk, (void*)coentry); 38 | } 39 | 40 | void co_delete(cothread_t cothread) { 41 | DeleteFiber(cothread); 42 | } 43 | 44 | void co_switch(cothread_t cothread) { 45 | co_active_ = cothread; 46 | SwitchToFiber(cothread); 47 | } 48 | 49 | #ifdef __cplusplus 50 | } 51 | #endif 52 | -------------------------------------------------------------------------------- /libco.c: -------------------------------------------------------------------------------- 1 | /* 2 | libco 3 | license: public domain 4 | */ 5 | 6 | #if defined(__clang__) || defined(__GNUC__) 7 | #if defined(__i386__) 8 | #include "x86.c" 9 | #elif defined(__amd64__) 10 | #include "amd64.c" 11 | #elif defined(__arm__) 12 | #include "arm.c" 13 | #elif defined(_ARCH_PPC) 14 | #include "ppc.c" 15 | #elif defined(_WIN32) 16 | #include "fiber.c" 17 | #else 18 | #include "sjlj.c" 19 | #endif 20 | #elif defined(_MSC_VER) 21 | #if defined(_M_IX86) 22 | #include "x86.c" 23 | #elif defined(_M_AMD64) 24 | #include "amd64.c" 25 | #else 26 | #include "fiber.c" 27 | #endif 28 | #else 29 | #error "libco: unsupported processor, compiler or operating system" 30 | #endif 31 | -------------------------------------------------------------------------------- /libco.h: -------------------------------------------------------------------------------- 1 | /* 2 | libco 3 | version: 0.17 (2015-06-18) 4 | author: byuu 5 | license: public domain 6 | */ 7 | 8 | #ifndef LIBCO_H 9 | #define LIBCO_H 10 | 11 | #ifdef LIBCO_C 12 | #ifdef LIBCO_MP 13 | #define thread_local __thread 14 | #else 15 | #define thread_local 16 | #endif 17 | #endif 18 | 19 | #ifdef __cplusplus 20 | extern "C" { 21 | #endif 22 | 23 | typedef void* cothread_t; 24 | 25 | cothread_t co_active(); 26 | cothread_t co_create(unsigned int, void (*)(void)); 27 | void co_delete(cothread_t); 28 | void co_switch(cothread_t); 29 | 30 | #ifdef __cplusplus 31 | } 32 | #endif 33 | 34 | /* ifndef LIBCO_H */ 35 | #endif 36 | -------------------------------------------------------------------------------- /ppc.c: -------------------------------------------------------------------------------- 1 | /* 2 | libco.ppc (2010-10-17) 3 | author: blargg 4 | license: public domain 5 | */ 6 | 7 | /* PowerPC 32/64 using embedded or external asm, with optional 8 | floating-point and AltiVec save/restore */ 9 | 10 | #define LIBCO_C 11 | #include "libco.h" 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | #define LIBCO_MPROTECT (__unix__ && !LIBCO_PPC_ASM) 18 | 19 | #if LIBCO_MPROTECT 20 | #include 21 | #include 22 | #endif 23 | 24 | /* State format (offsets in 32-bit words) 25 | 26 | +0 Pointer to swap code 27 | Rest of function descriptor for entry function 28 | +8 PC 29 | +10 SP 30 | Special regs 31 | GPRs 32 | FPRs 33 | VRs 34 | stack 35 | */ 36 | 37 | enum { state_size = 1024 }; 38 | enum { above_stack = 2048 }; 39 | enum { stack_align = 256 }; 40 | 41 | static thread_local cothread_t co_active_handle = 0; 42 | 43 | /**** Determine environment ****/ 44 | 45 | #define LIBCO_PPC64 (_ARCH_PPC64 || __PPC64__ || __ppc64__ || __powerpc64__) 46 | 47 | /* Whether function calls are indirect through a descriptor, 48 | or are directly to function */ 49 | #ifndef LIBCO_PPCDESC 50 | #if !_CALL_SYSV && (_CALL_AIX || _CALL_AIXDESC || LIBCO_PPC64) 51 | #define LIBCO_PPCDESC 1 52 | #endif 53 | #endif 54 | 55 | #ifdef LIBCO_PPC_ASM 56 | 57 | #ifdef __cplusplus 58 | extern "C" 59 | #endif 60 | 61 | /* Swap code is in ppc.S */ 62 | void co_swap_asm( cothread_t, cothread_t ); 63 | #define CO_SWAP_ASM( x, y ) co_swap_asm( x, y ) 64 | 65 | #else 66 | 67 | /* Swap code is here in array. Please leave dieassembly comments, 68 | as they make it easy to see what it does, and reorder instructions 69 | if one wants to see whether that improves performance. */ 70 | static const uint32_t libco_ppc_code [] = { 71 | #if LIBCO_PPC64 72 | 0x7d000026, /* mfcr r8 */ 73 | 0xf8240028, /* std r1,40(r4) */ 74 | 0x7d2802a6, /* mflr r9 */ 75 | 0xf9c40048, /* std r14,72(r4) */ 76 | 0xf9e40050, /* std r15,80(r4) */ 77 | 0xfa040058, /* std r16,88(r4) */ 78 | 0xfa240060, /* std r17,96(r4) */ 79 | 0xfa440068, /* std r18,104(r4) */ 80 | 0xfa640070, /* std r19,112(r4) */ 81 | 0xfa840078, /* std r20,120(r4) */ 82 | 0xfaa40080, /* std r21,128(r4) */ 83 | 0xfac40088, /* std r22,136(r4) */ 84 | 0xfae40090, /* std r23,144(r4) */ 85 | 0xfb040098, /* std r24,152(r4) */ 86 | 0xfb2400a0, /* std r25,160(r4) */ 87 | 0xfb4400a8, /* std r26,168(r4) */ 88 | 0xfb6400b0, /* std r27,176(r4) */ 89 | 0xfb8400b8, /* std r28,184(r4) */ 90 | 0xfba400c0, /* std r29,192(r4) */ 91 | 0xfbc400c8, /* std r30,200(r4) */ 92 | 0xfbe400d0, /* std r31,208(r4) */ 93 | 0xf9240020, /* std r9,32(r4) */ 94 | 0xe8e30020, /* ld r7,32(r3) */ 95 | 0xe8230028, /* ld r1,40(r3) */ 96 | 0x48000009, /* bl 1 */ 97 | 0x7fe00008, /* trap */ 98 | 0x91040030,/*1:stw r8,48(r4) */ 99 | 0x80c30030, /* lwz r6,48(r3) */ 100 | 0x7ce903a6, /* mtctr r7 */ 101 | 0xe9c30048, /* ld r14,72(r3) */ 102 | 0xe9e30050, /* ld r15,80(r3) */ 103 | 0xea030058, /* ld r16,88(r3) */ 104 | 0xea230060, /* ld r17,96(r3) */ 105 | 0xea430068, /* ld r18,104(r3) */ 106 | 0xea630070, /* ld r19,112(r3) */ 107 | 0xea830078, /* ld r20,120(r3) */ 108 | 0xeaa30080, /* ld r21,128(r3) */ 109 | 0xeac30088, /* ld r22,136(r3) */ 110 | 0xeae30090, /* ld r23,144(r3) */ 111 | 0xeb030098, /* ld r24,152(r3) */ 112 | 0xeb2300a0, /* ld r25,160(r3) */ 113 | 0xeb4300a8, /* ld r26,168(r3) */ 114 | 0xeb6300b0, /* ld r27,176(r3) */ 115 | 0xeb8300b8, /* ld r28,184(r3) */ 116 | 0xeba300c0, /* ld r29,192(r3) */ 117 | 0xebc300c8, /* ld r30,200(r3) */ 118 | 0xebe300d0, /* ld r31,208(r3) */ 119 | 0x7ccff120, /* mtcr r6 */ 120 | #else 121 | 0x7d000026, /* mfcr r8 */ 122 | 0x90240028, /* stw r1,40(r4) */ 123 | 0x7d2802a6, /* mflr r9 */ 124 | 0x91a4003c, /* stw r13,60(r4) */ 125 | 0x91c40040, /* stw r14,64(r4) */ 126 | 0x91e40044, /* stw r15,68(r4) */ 127 | 0x92040048, /* stw r16,72(r4) */ 128 | 0x9224004c, /* stw r17,76(r4) */ 129 | 0x92440050, /* stw r18,80(r4) */ 130 | 0x92640054, /* stw r19,84(r4) */ 131 | 0x92840058, /* stw r20,88(r4) */ 132 | 0x92a4005c, /* stw r21,92(r4) */ 133 | 0x92c40060, /* stw r22,96(r4) */ 134 | 0x92e40064, /* stw r23,100(r4) */ 135 | 0x93040068, /* stw r24,104(r4) */ 136 | 0x9324006c, /* stw r25,108(r4) */ 137 | 0x93440070, /* stw r26,112(r4) */ 138 | 0x93640074, /* stw r27,116(r4) */ 139 | 0x93840078, /* stw r28,120(r4) */ 140 | 0x93a4007c, /* stw r29,124(r4) */ 141 | 0x93c40080, /* stw r30,128(r4) */ 142 | 0x93e40084, /* stw r31,132(r4) */ 143 | 0x91240020, /* stw r9,32(r4) */ 144 | 0x80e30020, /* lwz r7,32(r3) */ 145 | 0x80230028, /* lwz r1,40(r3) */ 146 | 0x48000009, /* bl 1 */ 147 | 0x7fe00008, /* trap */ 148 | 0x91040030,/*1:stw r8,48(r4) */ 149 | 0x80c30030, /* lwz r6,48(r3) */ 150 | 0x7ce903a6, /* mtctr r7 */ 151 | 0x81a3003c, /* lwz r13,60(r3) */ 152 | 0x81c30040, /* lwz r14,64(r3) */ 153 | 0x81e30044, /* lwz r15,68(r3) */ 154 | 0x82030048, /* lwz r16,72(r3) */ 155 | 0x8223004c, /* lwz r17,76(r3) */ 156 | 0x82430050, /* lwz r18,80(r3) */ 157 | 0x82630054, /* lwz r19,84(r3) */ 158 | 0x82830058, /* lwz r20,88(r3) */ 159 | 0x82a3005c, /* lwz r21,92(r3) */ 160 | 0x82c30060, /* lwz r22,96(r3) */ 161 | 0x82e30064, /* lwz r23,100(r3) */ 162 | 0x83030068, /* lwz r24,104(r3) */ 163 | 0x8323006c, /* lwz r25,108(r3) */ 164 | 0x83430070, /* lwz r26,112(r3) */ 165 | 0x83630074, /* lwz r27,116(r3) */ 166 | 0x83830078, /* lwz r28,120(r3) */ 167 | 0x83a3007c, /* lwz r29,124(r3) */ 168 | 0x83c30080, /* lwz r30,128(r3) */ 169 | 0x83e30084, /* lwz r31,132(r3) */ 170 | 0x7ccff120, /* mtcr r6 */ 171 | #endif 172 | 173 | #ifndef LIBCO_PPC_NOFP 174 | 0xd9c400e0, /* stfd f14,224(r4) */ 175 | 0xd9e400e8, /* stfd f15,232(r4) */ 176 | 0xda0400f0, /* stfd f16,240(r4) */ 177 | 0xda2400f8, /* stfd f17,248(r4) */ 178 | 0xda440100, /* stfd f18,256(r4) */ 179 | 0xda640108, /* stfd f19,264(r4) */ 180 | 0xda840110, /* stfd f20,272(r4) */ 181 | 0xdaa40118, /* stfd f21,280(r4) */ 182 | 0xdac40120, /* stfd f22,288(r4) */ 183 | 0xdae40128, /* stfd f23,296(r4) */ 184 | 0xdb040130, /* stfd f24,304(r4) */ 185 | 0xdb240138, /* stfd f25,312(r4) */ 186 | 0xdb440140, /* stfd f26,320(r4) */ 187 | 0xdb640148, /* stfd f27,328(r4) */ 188 | 0xdb840150, /* stfd f28,336(r4) */ 189 | 0xdba40158, /* stfd f29,344(r4) */ 190 | 0xdbc40160, /* stfd f30,352(r4) */ 191 | 0xdbe40168, /* stfd f31,360(r4) */ 192 | 0xc9c300e0, /* lfd f14,224(r3) */ 193 | 0xc9e300e8, /* lfd f15,232(r3) */ 194 | 0xca0300f0, /* lfd f16,240(r3) */ 195 | 0xca2300f8, /* lfd f17,248(r3) */ 196 | 0xca430100, /* lfd f18,256(r3) */ 197 | 0xca630108, /* lfd f19,264(r3) */ 198 | 0xca830110, /* lfd f20,272(r3) */ 199 | 0xcaa30118, /* lfd f21,280(r3) */ 200 | 0xcac30120, /* lfd f22,288(r3) */ 201 | 0xcae30128, /* lfd f23,296(r3) */ 202 | 0xcb030130, /* lfd f24,304(r3) */ 203 | 0xcb230138, /* lfd f25,312(r3) */ 204 | 0xcb430140, /* lfd f26,320(r3) */ 205 | 0xcb630148, /* lfd f27,328(r3) */ 206 | 0xcb830150, /* lfd f28,336(r3) */ 207 | 0xcba30158, /* lfd f29,344(r3) */ 208 | 0xcbc30160, /* lfd f30,352(r3) */ 209 | 0xcbe30168, /* lfd f31,360(r3) */ 210 | #endif 211 | 212 | #ifdef __ALTIVEC__ 213 | 0x7ca042a6, /* mfvrsave r5 */ 214 | 0x39040180, /* addi r8,r4,384 */ 215 | 0x39240190, /* addi r9,r4,400 */ 216 | 0x70a00fff, /* andi. r0,r5,4095 */ 217 | 0x90a40034, /* stw r5,52(r4) */ 218 | 0x4182005c, /* beq- 2 */ 219 | 0x7e8041ce, /* stvx v20,r0,r8 */ 220 | 0x39080020, /* addi r8,r8,32 */ 221 | 0x7ea049ce, /* stvx v21,r0,r9 */ 222 | 0x39290020, /* addi r9,r9,32 */ 223 | 0x7ec041ce, /* stvx v22,r0,r8 */ 224 | 0x39080020, /* addi r8,r8,32 */ 225 | 0x7ee049ce, /* stvx v23,r0,r9 */ 226 | 0x39290020, /* addi r9,r9,32 */ 227 | 0x7f0041ce, /* stvx v24,r0,r8 */ 228 | 0x39080020, /* addi r8,r8,32 */ 229 | 0x7f2049ce, /* stvx v25,r0,r9 */ 230 | 0x39290020, /* addi r9,r9,32 */ 231 | 0x7f4041ce, /* stvx v26,r0,r8 */ 232 | 0x39080020, /* addi r8,r8,32 */ 233 | 0x7f6049ce, /* stvx v27,r0,r9 */ 234 | 0x39290020, /* addi r9,r9,32 */ 235 | 0x7f8041ce, /* stvx v28,r0,r8 */ 236 | 0x39080020, /* addi r8,r8,32 */ 237 | 0x7fa049ce, /* stvx v29,r0,r9 */ 238 | 0x39290020, /* addi r9,r9,32 */ 239 | 0x7fc041ce, /* stvx v30,r0,r8 */ 240 | 0x7fe049ce, /* stvx v31,r0,r9 */ 241 | 0x80a30034,/*2:lwz r5,52(r3) */ 242 | 0x39030180, /* addi r8,r3,384 */ 243 | 0x39230190, /* addi r9,r3,400 */ 244 | 0x70a00fff, /* andi. r0,r5,4095 */ 245 | 0x7ca043a6, /* mtvrsave r5 */ 246 | 0x4d820420, /* beqctr */ 247 | 0x7e8040ce, /* lvx v20,r0,r8 */ 248 | 0x39080020, /* addi r8,r8,32 */ 249 | 0x7ea048ce, /* lvx v21,r0,r9 */ 250 | 0x39290020, /* addi r9,r9,32 */ 251 | 0x7ec040ce, /* lvx v22,r0,r8 */ 252 | 0x39080020, /* addi r8,r8,32 */ 253 | 0x7ee048ce, /* lvx v23,r0,r9 */ 254 | 0x39290020, /* addi r9,r9,32 */ 255 | 0x7f0040ce, /* lvx v24,r0,r8 */ 256 | 0x39080020, /* addi r8,r8,32 */ 257 | 0x7f2048ce, /* lvx v25,r0,r9 */ 258 | 0x39290020, /* addi r9,r9,32 */ 259 | 0x7f4040ce, /* lvx v26,r0,r8 */ 260 | 0x39080020, /* addi r8,r8,32 */ 261 | 0x7f6048ce, /* lvx v27,r0,r9 */ 262 | 0x39290020, /* addi r9,r9,32 */ 263 | 0x7f8040ce, /* lvx v28,r0,r8 */ 264 | 0x39080020, /* addi r8,r8,32 */ 265 | 0x7fa048ce, /* lvx v29,r0,r9 */ 266 | 0x39290020, /* addi r9,r9,32 */ 267 | 0x7fc040ce, /* lvx v30,r0,r8 */ 268 | 0x7fe048ce, /* lvx v31,r0,r9 */ 269 | #endif 270 | 271 | 0x4e800420, /* bctr */ 272 | }; 273 | 274 | #if LIBCO_PPCDESC 275 | /* Function call goes through indirect descriptor */ 276 | #define CO_SWAP_ASM( x, y ) \ 277 | ((void (*)( cothread_t, cothread_t )) (uintptr_t) x)( x, y ) 278 | #else 279 | /* Function call goes directly to code */ 280 | #define CO_SWAP_ASM( x, y ) \ 281 | ((void (*)( cothread_t, cothread_t )) (uintptr_t) libco_ppc_code)( x, y ) 282 | #endif 283 | 284 | #endif 285 | 286 | static uint32_t* co_create_( unsigned size, uintptr_t entry ) 287 | { 288 | uint32_t* t = (uint32_t*) malloc( size ); 289 | 290 | (void) entry; 291 | 292 | #if LIBCO_PPCDESC 293 | if ( t ) 294 | { 295 | /* Copy entry's descriptor */ 296 | memcpy( t, (void*) entry, sizeof (void*) * 3 ); 297 | 298 | /* Set function pointer to swap routine */ 299 | #ifdef LIBCO_PPC_ASM 300 | *(const void**) t = *(void**) &co_swap_asm; 301 | #else 302 | *(const void**) t = libco_ppc_code; 303 | #endif 304 | } 305 | #endif 306 | 307 | return t; 308 | } 309 | 310 | cothread_t co_create( unsigned int size, void (*entry_)( void ) ) 311 | { 312 | uintptr_t entry = (uintptr_t) entry_; 313 | uint32_t* t = NULL; 314 | 315 | /* Be sure main thread was successfully allocated */ 316 | if ( co_active() ) 317 | { 318 | size += state_size + above_stack + stack_align; 319 | t = co_create_( size, entry ); 320 | } 321 | 322 | if ( t ) 323 | { 324 | uintptr_t sp; 325 | int shift; 326 | 327 | /* Save current registers into new thread, so that any special ones will 328 | have proper values when thread is begun */ 329 | CO_SWAP_ASM( t, t ); 330 | 331 | #if LIBCO_PPCDESC 332 | /* Get real address */ 333 | entry = (uintptr_t) *(void**) entry; 334 | #endif 335 | 336 | /* Put stack near end of block, and align */ 337 | sp = (uintptr_t) t + size - above_stack; 338 | sp -= sp % stack_align; 339 | 340 | /* On PPC32, we save and restore GPRs as 32 bits. For PPC64, we 341 | save and restore them as 64 bits, regardless of the size the ABI 342 | uses. So, we manually write pointers at the proper size. We always 343 | save and restore at the same address, and since PPC is big-endian, 344 | we must put the low byte first on PPC32. */ 345 | 346 | /* If uintptr_t is 32 bits, >>32 is undefined behavior, so we do two shifts 347 | and don't have to care how many bits uintptr_t is. */ 348 | #if LIBCO_PPC64 349 | shift = 16; 350 | #else 351 | shift = 0; 352 | #endif 353 | 354 | /* Set up so entry will be called on next swap */ 355 | t [8] = (uint32_t) (entry >> shift >> shift); 356 | t [9] = (uint32_t) entry; 357 | 358 | t [10] = (uint32_t) (sp >> shift >> shift); 359 | t [11] = (uint32_t) sp; 360 | } 361 | 362 | return t; 363 | } 364 | 365 | void co_delete( cothread_t t ) 366 | { 367 | free( t ); 368 | } 369 | 370 | static void co_init_( void ) 371 | { 372 | #if LIBCO_MPROTECT 373 | /* TODO: pre- and post-pad PPC code so that this doesn't make other 374 | data executable and writable */ 375 | long page_size = sysconf( _SC_PAGESIZE ); 376 | if ( page_size > 0 ) 377 | { 378 | uintptr_t align = page_size; 379 | uintptr_t begin = (uintptr_t) libco_ppc_code; 380 | uintptr_t end = begin + sizeof libco_ppc_code; 381 | 382 | /* Align beginning and end */ 383 | end += align - 1; 384 | end -= end % align; 385 | begin -= begin % align; 386 | 387 | mprotect( (void*) begin, end - begin, PROT_READ | PROT_WRITE | PROT_EXEC ); 388 | } 389 | #endif 390 | 391 | co_active_handle = co_create_( state_size, (uintptr_t) &co_switch ); 392 | } 393 | 394 | cothread_t co_active() 395 | { 396 | if ( !co_active_handle ) 397 | co_init_(); 398 | 399 | return co_active_handle; 400 | } 401 | 402 | void co_switch( cothread_t t ) 403 | { 404 | cothread_t old = co_active_handle; 405 | co_active_handle = t; 406 | 407 | CO_SWAP_ASM( t, old ); 408 | } 409 | -------------------------------------------------------------------------------- /sjlj.c: -------------------------------------------------------------------------------- 1 | /* 2 | libco.sjlj (2008-01-28) 3 | author: Nach 4 | license: public domain 5 | */ 6 | 7 | /* 8 | * Note this was designed for UNIX systems. Based on ideas expressed in a paper 9 | * by Ralf Engelschall. 10 | * For SJLJ on other systems, one would want to rewrite springboard() and 11 | * co_create() and hack the jmb_buf stack pointer. 12 | */ 13 | 14 | #define LIBCO_C 15 | #include "libco.h" 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | #ifdef __cplusplus 22 | extern "C" { 23 | #endif 24 | 25 | typedef struct { 26 | sigjmp_buf context; 27 | void (*coentry)(void); 28 | void *stack; 29 | } cothread_struct; 30 | 31 | static thread_local cothread_struct co_primary; 32 | static thread_local cothread_struct *creating, *co_running = 0; 33 | 34 | static void springboard(int ignored) { 35 | if(sigsetjmp(creating->context, 0)) { 36 | co_running->coentry(); 37 | } 38 | } 39 | 40 | cothread_t co_active() { 41 | if(!co_running) co_running = &co_primary; 42 | return (cothread_t)co_running; 43 | } 44 | 45 | cothread_t co_create(unsigned int size, void (*coentry)(void)) { 46 | if(!co_running) co_running = &co_primary; 47 | 48 | cothread_struct *thread = (cothread_struct*)malloc(sizeof(cothread_struct)); 49 | if(thread) { 50 | struct sigaction handler; 51 | struct sigaction old_handler; 52 | 53 | stack_t stack; 54 | stack_t old_stack; 55 | 56 | thread->coentry = thread->stack = 0; 57 | 58 | stack.ss_flags = 0; 59 | stack.ss_size = size; 60 | thread->stack = stack.ss_sp = malloc(size); 61 | if(stack.ss_sp && !sigaltstack(&stack, &old_stack)) { 62 | handler.sa_handler = springboard; 63 | handler.sa_flags = SA_ONSTACK; 64 | sigemptyset(&handler.sa_mask); 65 | creating = thread; 66 | 67 | if(!sigaction(SIGUSR1, &handler, &old_handler)) { 68 | if(!raise(SIGUSR1)) { 69 | thread->coentry = coentry; 70 | } 71 | sigaltstack(&old_stack, 0); 72 | sigaction(SIGUSR1, &old_handler, 0); 73 | } 74 | } 75 | 76 | if(thread->coentry != coentry) { 77 | co_delete(thread); 78 | thread = 0; 79 | } 80 | } 81 | 82 | return (cothread_t)thread; 83 | } 84 | 85 | void co_delete(cothread_t cothread) { 86 | if(cothread) { 87 | if(((cothread_struct*)cothread)->stack) { 88 | free(((cothread_struct*)cothread)->stack); 89 | } 90 | free(cothread); 91 | } 92 | } 93 | 94 | void co_switch(cothread_t cothread) { 95 | if(!sigsetjmp(co_running->context, 0)) { 96 | co_running = (cothread_struct*)cothread; 97 | siglongjmp(co_running->context, 1); 98 | } 99 | } 100 | 101 | #ifdef __cplusplus 102 | } 103 | #endif 104 | -------------------------------------------------------------------------------- /ucontext.c: -------------------------------------------------------------------------------- 1 | /* 2 | libco.ucontext (2008-01-28) 3 | author: Nach 4 | license: public domain 5 | */ 6 | 7 | /* 8 | * WARNING: the overhead of POSIX ucontext is very high, 9 | * assembly versions of libco or libco_sjlj should be much faster 10 | * 11 | * This library only exists for two reasons: 12 | * 1 - as an initial test for the viability of a ucontext implementation 13 | * 2 - to demonstrate the power and speed of libco over existing implementations, 14 | * such as pth (which defaults to wrapping ucontext on unix targets) 15 | * 16 | * Use this library only as a *last resort* 17 | */ 18 | 19 | #define LIBCO_C 20 | #include "libco.h" 21 | 22 | #define _BSD_SOURCE 23 | #include 24 | #include 25 | 26 | #ifdef __cplusplus 27 | extern "C" { 28 | #endif 29 | 30 | static thread_local ucontext_t co_primary; 31 | static thread_local ucontext_t *co_running = 0; 32 | 33 | cothread_t co_active() { 34 | if(!co_running) co_running = &co_primary; 35 | return (cothread_t)co_running; 36 | } 37 | 38 | cothread_t co_create(unsigned int heapsize, void (*coentry)(void)) { 39 | if(!co_running) co_running = &co_primary; 40 | ucontext_t *thread = (ucontext_t*)malloc(sizeof(ucontext_t)); 41 | if(thread) { 42 | if((!getcontext(thread) && !(thread->uc_stack.ss_sp = 0)) && (thread->uc_stack.ss_sp = malloc(heapsize))) { 43 | thread->uc_link = co_running; 44 | thread->uc_stack.ss_size = heapsize; 45 | makecontext(thread, coentry, 0); 46 | } else { 47 | co_delete((cothread_t)thread); 48 | thread = 0; 49 | } 50 | } 51 | return (cothread_t)thread; 52 | } 53 | 54 | void co_delete(cothread_t cothread) { 55 | if(cothread) { 56 | if(((ucontext_t*)cothread)->uc_stack.ss_sp) { free(((ucontext_t*)cothread)->uc_stack.ss_sp); } 57 | free(cothread); 58 | } 59 | } 60 | 61 | void co_switch(cothread_t cothread) { 62 | ucontext_t *old_thread = co_running; 63 | co_running = (ucontext_t*)cothread; 64 | swapcontext(old_thread, co_running); 65 | } 66 | 67 | #ifdef __cplusplus 68 | } 69 | #endif 70 | -------------------------------------------------------------------------------- /x86.c: -------------------------------------------------------------------------------- 1 | /* 2 | libco.x86 (2009-10-12) 3 | author: byuu 4 | license: public domain 5 | */ 6 | 7 | #define LIBCO_C 8 | #include "libco.h" 9 | 10 | #include 11 | #include 12 | 13 | #ifdef __cplusplus 14 | extern "C" { 15 | #endif 16 | 17 | #if defined(__clang__) || defined(__GNUC__) 18 | #define fastcall __attribute__((fastcall)) 19 | #elif defined(_MSC_VER) 20 | #define fastcall __fastcall 21 | #else 22 | #error "libco: please define fastcall macro" 23 | #endif 24 | 25 | static thread_local long co_active_buffer[64]; 26 | static thread_local cothread_t co_active_handle = 0; 27 | static void (fastcall *co_swap)(cothread_t, cothread_t) = 0; 28 | 29 | /* ABI: fastcall */ 30 | static unsigned char co_swap_function[] = { 31 | 0x89, 0x22, /* mov [edx],esp */ 32 | 0x8b, 0x21, /* mov esp,[ecx] */ 33 | 0x58, /* pop eax */ 34 | 0x89, 0x6a, 0x04, /* mov [edx+ 4],ebp */ 35 | 0x89, 0x72, 0x08, /* mov [edx+ 8],esi */ 36 | 0x89, 0x7a, 0x0c, /* mov [edx+12],edi */ 37 | 0x89, 0x5a, 0x10, /* mov [edx+16],ebx */ 38 | 0x8b, 0x69, 0x04, /* mov ebp,[ecx+ 4] */ 39 | 0x8b, 0x71, 0x08, /* mov esi,[ecx+ 8] */ 40 | 0x8b, 0x79, 0x0c, /* mov edi,[ecx+12] */ 41 | 0x8b, 0x59, 0x10, /* mov ebx,[ecx+16] */ 42 | 0xff, 0xe0, /* jmp eax */ 43 | }; 44 | 45 | #ifdef _WIN32 46 | #include 47 | 48 | void co_init() { 49 | DWORD old_privileges; 50 | VirtualProtect(co_swap_function, sizeof co_swap_function, PAGE_EXECUTE_READWRITE, &old_privileges); 51 | } 52 | #else 53 | #include 54 | #include 55 | 56 | void co_init() { 57 | unsigned long addr = (unsigned long)co_swap_function; 58 | unsigned long base = addr - (addr % sysconf(_SC_PAGESIZE)); 59 | unsigned long size = (addr - base) + sizeof co_swap_function; 60 | mprotect((void*)base, size, PROT_READ | PROT_WRITE | PROT_EXEC); 61 | } 62 | #endif 63 | 64 | static void crash() { 65 | assert(0); /* called only if cothread_t entrypoint returns */ 66 | } 67 | 68 | cothread_t co_active() { 69 | if(!co_active_handle) co_active_handle = &co_active_buffer; 70 | return co_active_handle; 71 | } 72 | 73 | cothread_t co_create(unsigned int size, void (*entrypoint)(void)) { 74 | cothread_t handle; 75 | if(!co_swap) { 76 | co_init(); 77 | co_swap = (void (fastcall*)(cothread_t, cothread_t))co_swap_function; 78 | } 79 | if(!co_active_handle) co_active_handle = &co_active_buffer; 80 | size += 256; /* allocate additional space for storage */ 81 | size &= ~15; /* align stack to 16-byte boundary */ 82 | 83 | if(handle = (cothread_t)malloc(size)) { 84 | long *p = (long*)((char*)handle + size); /* seek to top of stack */ 85 | *--p = (long)crash; /* crash if entrypoint returns */ 86 | *--p = (long)entrypoint; /* start of function */ 87 | *(long*)handle = (long)p; /* stack pointer */ 88 | } 89 | 90 | return handle; 91 | } 92 | 93 | void co_delete(cothread_t handle) { 94 | free(handle); 95 | } 96 | 97 | void co_switch(cothread_t handle) { 98 | register cothread_t co_previous_handle = co_active_handle; 99 | co_swap(co_active_handle = handle, co_previous_handle); 100 | } 101 | 102 | #ifdef __cplusplus 103 | } 104 | #endif 105 | --------------------------------------------------------------------------------