├── LICENSE ├── Makefile ├── README ├── benchco.c ├── coroutine.h ├── coroutine_create.c ├── coroutine_free.c ├── coroutine_yield-x86-32.asm ├── coroutine_yield-x86-64.asm ├── iter.c ├── megaco.c └── test.c /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ######################################################################### 2 | # 3 | # Copyright 2017 by Sean Conner. All Rights Reserved. 4 | # 5 | # This library is free software; you can redistribute it and/or modify it 6 | # under the terms of the GNU Lesser General Public License as published by 7 | # the Free Software Foundation; either version 3 of the License, or (at your 8 | # option) any later version. 9 | # 10 | # This library is distributed in the hope that it will be useful, but 11 | # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 12 | # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 13 | # License for more details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public License 16 | # along with this library; if not, see . 17 | # 18 | # Comments, questions and criticisms can be sent to: sean@conman.org 19 | # 20 | ######################################################################## 21 | 22 | UNAME := $(shell uname) 23 | 24 | ifeq ($(UNAME),Darwin) 25 | OBJS = coroutine_yield-x86-64.o 26 | FORMAT32 = macho32 27 | FORMAT64 = macho64 28 | endif 29 | 30 | ifeq ($(UNAME),Linux) 31 | OBJS = coroutine_yield-x86-32.o coroutine_yield-x86-64.o 32 | FORMAT32 = elf32 33 | FORMAT64 = elf64 34 | endif 35 | 36 | CC = c99 37 | CFLAGS = -g 38 | LDFLAGS = -g 39 | LDLIBS = 40 | ASM = nasm -f $(FORMAT32) 41 | ASMFLAGS = -g 42 | AR = ar rscu 43 | 44 | %.a : 45 | $(AR) $@ $? 46 | 47 | %.o : %.asm 48 | $(ASM) $(ASMFLAGS) -l $(*F).list -o $@ $< 49 | 50 | .PHONY : all clean 51 | 52 | all : test iter megaco benchco 53 | clean: 54 | $(RM) test iter megaco benchco *.o *~ *.a core.* *.list 55 | 56 | test : test.o libco.a 57 | iter : iter.o libco.a 58 | megaco : megaco.o libco.a 59 | megaco : LDLIBS = -lrt 60 | benchco : benchco.o libco.a 61 | benchco : LDLIBS = -lrt 62 | libco.a : coroutine_create.o coroutine_free.o $(OBJS) 63 | 64 | coroutine_yield-x86-64.o : ASM = nasm -f $(FORMAT64) 65 | coroutine_create.o : coroutine.h 66 | coroutine_free.o : coroutine.h 67 | test.o : coroutine.h 68 | iter.o : coroutine.h 69 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | 2 | An Implementaion of Coroutines for C 3 | 4 | This is not based on setjmp()/longjmp() or ucontext.h but is a ground-up 5 | implementation of coroutines for C. This involves the use of assembly to 6 | ensure the coroutine stack intialization and stack switching. The code may 7 | look simple, but it wasn't simple to write. but it is straightforward 8 | (mostly). 9 | 10 | There are two example programs, test and iter. test just runs some simple 11 | tests while iter will create a tree from lines of input (either stdin or a 12 | file), then use a coroutine to iterate through the tree. 13 | 14 | API: 15 | 16 | extern int coroutine_create( 17 | coroutine__s **pco, 18 | size_t stsize, 19 | uintptr_t (*fun)(coroutine__s *,uintptr_t) 20 | ); 21 | 22 | Creates a coroutine context, which will include a stack of stsize; 23 | if stsize is 0, a default size is automatically provided (which 24 | *should* be big enough for most uses). This will return 0 on 25 | success, non-zero on failure. 26 | 27 | The function passed in takes two parameters, a coroutine__s * (which 28 | will refer to its own context when called) and a uintptr_t (an 29 | integer quantity large enough to hold a pointer). It can return 30 | values back via coroutine_yield() or simply returning a value. Once 31 | the function returns, it will not be called on subsequent calls to 32 | coroutine_yield() (see coroutine_yield() for more information). 33 | 34 | extern void coroutine_init( 35 | coroutine__s *co, 36 | uintptr_t (*fun)(coroutine__s *,uintptr_t), 37 | void *stack 38 | ); 39 | 40 | Initializes the coroutine stack so that the first call to 41 | coroutine_yield() will call the given function. This is not meant 42 | to be called from user code but is rather an internal method to 43 | coroutine_create(). The stack paramter should point to the stack 44 | top (highest memory address on most systems). 45 | 46 | extern uintptr_t coroutine_yield( 47 | coroutine__s *co, 48 | uintptr_t data 49 | ); 50 | 51 | Passes control to the coroutine, passing data to it. This call is 52 | symetrical, being used on both sides to pass control back and forth. 53 | Once the coroutine's main function does an actual return, further 54 | calls to coroutine_yield() to said coroutine will immediately yield 55 | back, returning data directly. 56 | 57 | NOTE: setjmp()/longjmp() will *NOT* work across coroutine stacks. 58 | setjmp()/longjmp() will work within a coroutine stack. 59 | 60 | extern int coroutine_free( 61 | coroutine__s *co 62 | ); 63 | 64 | Free up the memory used for co and its stack. No other resources 65 | are reclaimed (like open files). calling coroutine_free() on a 66 | coroutine currently running is undefined (if you are lucky, the 67 | program will crash immediate). The coroutine need not be finished 68 | running when this is called. 69 | -------------------------------------------------------------------------------- /benchco.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | 3 | #include 4 | #include 5 | #include 6 | #include "coroutine.h" 7 | 8 | #ifdef __APPLE__ 9 | # error Mac OS-X does not support clock_gettime() 10 | #endif 11 | 12 | static uintptr_t subtask(coroutine__s *self,uintptr_t data) 13 | { 14 | while(data--) 15 | data = coroutine_yield(self,data); 16 | return 0; 17 | } 18 | 19 | /*************************************************************************/ 20 | 21 | static void runtest(struct timespec *result,coroutine__s *co,uintptr_t data) 22 | { 23 | struct timespec start; 24 | struct timespec end; 25 | 26 | clock_gettime(CLOCK_MONOTONIC,&start); 27 | while(data--) 28 | data = coroutine_yield(co,data); 29 | clock_gettime(CLOCK_MONOTONIC,&end); 30 | 31 | if (end.tv_nsec < start.tv_nsec) 32 | { 33 | end.tv_nsec += 1000000000L; 34 | end.tv_sec--; 35 | } 36 | 37 | result->tv_nsec = end.tv_nsec - start.tv_nsec; 38 | result->tv_sec = end.tv_sec - start.tv_sec; 39 | } 40 | 41 | /*************************************************************************/ 42 | 43 | int main(int argc,char *argv[]) 44 | { 45 | struct timespec total; 46 | coroutine__s *co; 47 | uintptr_t max; 48 | unsigned long long bav; 49 | long res; 50 | 51 | if (argc == 1) 52 | max = 1000000uL; 53 | else 54 | max = strtoul(argv[1],NULL,10); 55 | 56 | coroutine_create(&co,0,subtask); 57 | runtest(&total,co,max); 58 | bav = (unsigned long long)total.tv_sec * 1000000000uLL 59 | + (unsigned long long)total.tv_nsec; 60 | res = (unsigned long)(bav / (unsigned long long)max); 61 | res /= 2; /* adjust for both coroutines adjusting count */ 62 | 63 | printf("Initial coroutine_yield() %ld %ld %ld\n",(long)total.tv_sec,(long)total.tv_nsec,res); 64 | 65 | coroutine_free(co); 66 | return 0; 67 | } 68 | -------------------------------------------------------------------------------- /coroutine.h: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | * 3 | * Copyright 2017 by Sean Conner. 4 | * 5 | * This library is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU Lesser General Public License as published by 7 | * the Free Software Foundation; either version 3 of the License, or (at your 8 | * option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, but 11 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 12 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 13 | * License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public License 16 | * along with this library; if not, see . 17 | * 18 | * Comments, questions and criticisms can be sent to: sean@conman.org 19 | * 20 | *************************************************************************/ 21 | 22 | #ifndef I_5A22037D_5F90_5456_A562_661022F242BF 23 | #define I_5A22037D_5F90_5456_A562_661022F242BF 24 | 25 | #include 26 | #include 27 | 28 | #if defined(__i386) 29 | # define coroutine_init coroutine_init32 30 | # define coroutine_yield coroutine_yield32 31 | #elif defined(__x86) 32 | # define coroutine_init coroutine_init32 33 | # define coroutine_yield coroutine_yield32 34 | #elif defined(__x86_64) 35 | # define coroutine_init coroutine_init64 36 | # define coroutine_yield coroutine_yield64 37 | #endif 38 | 39 | typedef struct 40 | { 41 | void *csp; 42 | void *base; 43 | size_t size; 44 | void *user; 45 | } coroutine__s; 46 | 47 | 48 | extern int coroutine_create(coroutine__s **,size_t,uintptr_t (*fun)(coroutine__s *,uintptr_t)); 49 | extern uintptr_t coroutine_yield (coroutine__s *,uintptr_t); 50 | extern int coroutine_free (coroutine__s *); 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /coroutine_create.c: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | * 3 | * Copyright 2017 by Sean Conner. 4 | * 5 | * This library is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU Lesser General Public License as published by 7 | * the Free Software Foundation; either version 3 of the License, or (at your 8 | * option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, but 11 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 12 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 13 | * License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public License 16 | * along with this library; if not, see . 17 | * 18 | * Comments, questions and criticisms can be sent to: sean@conman.org 19 | * 20 | *************************************************************************/ 21 | 22 | #include 23 | #include 24 | #include "coroutine.h" 25 | 26 | extern void coroutine_init( 27 | coroutine__s *, 28 | uintptr_t (*)(coroutine__s *,uintptr_t), 29 | void * 30 | ); 31 | 32 | int coroutine_create( 33 | coroutine__s **pco, 34 | size_t stsize, 35 | uintptr_t (*fun)(coroutine__s *,uintptr_t) 36 | ) 37 | { 38 | char *blob; 39 | coroutine__s *co; 40 | 41 | assert(pco != NULL); 42 | assert(fun); 43 | 44 | if (stsize == 0) 45 | stsize = 8192; 46 | 47 | if ((blob = malloc(stsize)) == NULL) 48 | return -1; 49 | 50 | co = (coroutine__s *)&blob[stsize - sizeof(coroutine__s)]; 51 | co->base = blob; 52 | co->size = stsize; 53 | 54 | coroutine_init(co,fun,co); 55 | *pco = co; 56 | return 0; 57 | } 58 | 59 | -------------------------------------------------------------------------------- /coroutine_free.c: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | * 3 | * Copyright 2017 by Sean Conner. 4 | * 5 | * This library is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU Lesser General Public License as published by 7 | * the Free Software Foundation; either version 3 of the License, or (at your 8 | * option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, but 11 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 12 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 13 | * License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public License 16 | * along with this library; if not, see . 17 | * 18 | * Comments, questions and criticisms can be sent to: sean@conman.org 19 | * 20 | *************************************************************************/ 21 | 22 | #include 23 | #include 24 | #include "coroutine.h" 25 | 26 | int coroutine_free(coroutine__s *co) 27 | { 28 | assert(co != NULL); 29 | assert(co->base != NULL); 30 | assert(co->size > 0); 31 | 32 | free(co->base); 33 | return 0; 34 | } 35 | -------------------------------------------------------------------------------- /coroutine_yield-x86-32.asm: -------------------------------------------------------------------------------- 1 | ;*************************************************************************** 2 | ; 3 | ; Copyright 2017 by Sean Conner. 4 | ; 5 | ; This library is free software; you can redistribute it and/or modify it 6 | ; under the terms of the GNU Lesser General Public License as published by 7 | ; the Free Software Foundation; either version 3 of the License, or (at your 8 | ; option) any later version. 9 | ; 10 | ; This library is distributed in the hope that it will be useful, but 11 | ; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 12 | ; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 13 | ; License for more details. 14 | ; 15 | ; You should have received a copy of the GNU Lesser General Public License 16 | ; along with this library; if not, see . 17 | ; 18 | ; Comments, questions and criticisms can be sent to: sean@conman.org 19 | ; 20 | ;*************************************************************************** 21 | 22 | bits 32 23 | global coroutine_init32 24 | global coroutine_yield32 25 | 26 | ;*************************************************************************** 27 | section .text 28 | 29 | ;=========================================================================== 30 | ; The stack frame for this routine has been set up *AS IF* the following 31 | ; code had been executed upon entry: 32 | ; 33 | ; enter -8,0 34 | ; mov eax,[ebp + P_co] 35 | ; mov [ebp + L_co],eax 36 | ; mov eax,[ebp + P_fun] 37 | ; mov [ebp + L_fun],eax 38 | ; call some_function_somewhere 39 | ; 40 | ; The code assumes we've just called some_function_somewhere() to retrieve 41 | ; some sort of return code, and then is calling L_fun right afterwards. The 42 | ; only difference is that in reality, the parameters, return address and 43 | ; previous EBP don't actually exist---just keep that in mind. 44 | ; 45 | ; After L_fun returns, we return it's final return code using 46 | ; coroutine_yield(). How we handle a coroutine_resume() after that is still 47 | ; up in the air. For right now, simply return the passed in value back to 48 | ; coroutine_yield(). I could call abort() ... I don't know ... 49 | ;--------------------------------------------------------------------------- 50 | 51 | %assign L_co -4 52 | %assign L_fun -8 53 | %assign C_param -12 54 | 55 | start_it_up: push eax 56 | push dword [ebp + L_co] 57 | call [ebp + L_fun] 58 | 59 | do_it_again: mov [ebp + C_param],eax 60 | call coroutine_yield32 61 | jmp do_it_again 62 | 63 | ;=========================================================================== 64 | 65 | %assign P_stack 12 66 | %assign P_fun 8 67 | %assign P_co 4 68 | 69 | coroutine_init32: 70 | mov edx,[esp + P_co] 71 | mov eax,[esp + P_stack] ; stack to switch to 72 | 73 | ;------------------------------------------------------------ 74 | ; Create the stack for resuming to start_it_up(). The stack 75 | ; is set up as: 76 | ; 77 | ; EAX -> 78 | ; +--------------------------+ 79 | ; | L_co | 80 | ; +--------------------------+ 81 | ; | L_fun | 82 | ; +--------------------------+ 83 | ; | start_it_up | 84 | ; +--------------------------+ 85 | ; co EBP -> | EBP of start_it_up (EAX) | 86 | ; +--------------------------+ 87 | ; | "saved" EBX (0) | 88 | ; +--------------------------+ 89 | ; | "saved" ESI (0) | 90 | ; +--------------------------+ 91 | ; co ESP -> | "saved" EDI (0) | 92 | ; +--------------------------+ 93 | ; 94 | ; The code in coroutine.resume() will pop the three registers off 95 | ; the stack, then restore EBP/ESP and "return" to start_it_up. 96 | ;------------------------------------------------------------ 97 | 98 | lea ecx,[eax - 28] 99 | mov [ecx + 12],eax ; EBP of coroutine 100 | mov [ecx + 24],edx ; L_co 101 | mov eax,[esp + P_fun] ; L_fun 102 | mov [ecx + 20],eax 103 | mov dword [ecx + 16],start_it_up 104 | xor eax,eax 105 | mov [ecx + 8],eax ; "saved" EBX 106 | mov [ecx + 4],eax ; "saved" ESI 107 | mov [ecx + 0],eax ; "saved" EDI 108 | mov [edx],ecx 109 | ret 110 | 111 | ;=========================================================================== 112 | 113 | %assign P_param 8 + 16 114 | %assign P_co 4 + 16 115 | 116 | coroutine_yield32: 117 | push ebp ; save callee saved registers 118 | push ebx 119 | push esi 120 | push edi 121 | 122 | mov eax,[esp + P_param] ; return parameter 123 | mov edx,[esp + P_co] ; get stack to yield to 124 | xchg esp,[edx] ; YIELD! 125 | 126 | pop edi ; retore registers 127 | pop esi 128 | pop ebx 129 | pop ebp 130 | ret 131 | 132 | ;*************************************************************************** 133 | -------------------------------------------------------------------------------- /coroutine_yield-x86-64.asm: -------------------------------------------------------------------------------- 1 | ;*************************************************************************** 2 | ; 3 | ; Copyright 2017 by Sean Conner. 4 | ; 5 | ; This library is free software; you can redistribute it and/or modify it 6 | ; under the terms of the GNU Lesser General Public License as published by 7 | ; the Free Software Foundation; either version 3 of the License, or (at your 8 | ; option) any later version. 9 | ; 10 | ; This library is distributed in the hope that it will be useful, but 11 | ; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 12 | ; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 13 | ; License for more details. 14 | ; 15 | ; You should have received a copy of the GNU Lesser General Public License 16 | ; along with this library; if not, see . 17 | ; 18 | ; Comments, questions and criticisms can be sent to: sean@conman.org 19 | ; 20 | ;*************************************************************************** 21 | 22 | bits 64 23 | default rel 24 | global coroutine_init64 25 | global coroutine_yield64 26 | global _coroutine_init64 ; for Mac OS-X 27 | global _coroutine_yield64 ; (how archaic!) 28 | 29 | ;*************************************************************************** 30 | section .text 31 | 32 | ;=========================================================================== 33 | 34 | %assign L_co -8 35 | %assign L_fun -16 36 | 37 | start_it_up: mov rdi,[rbp + L_co] 38 | mov rsi,rax 39 | call [rbp + L_fun] 40 | 41 | do_it_again: mov rdi,[rbp + L_co] 42 | mov rsi,rax 43 | call coroutine_yield64 44 | jmp do_it_again 45 | 46 | ;=========================================================================== 47 | ; 48 | ; RDI -> co 49 | ; RSI -> fun 50 | ; RDX -> stack 51 | ; RAX -> 0 52 | ; RCX -> adjusted stack 53 | ; 54 | ; L_co 64 55 | ; L_run 56 56 | ; start_it_up 48 57 | ; rbp 40 58 | ; rbx 32 59 | ; r12 24 60 | ; r13 16 61 | ; r14 8 62 | ; r15 0 63 | ; 64 | 65 | coroutine_init64: 66 | _coroutine_init64: 67 | lea rax,[start_it_up] 68 | lea rcx,[rdx - 9*8] 69 | 70 | mov [rcx + 64],rdi ; L_co 71 | mov [rcx + 56],rsi ; L_fun 72 | mov [rcx + 48],rax ; start_it_up 73 | mov [rcx + 40],rdx ; "saved" rbp 74 | xor eax,eax 75 | mov [rcx + 32],rax ; "saved" rbx 76 | mov [rcx + 24],rax ; "saved" r12 77 | mov [rcx + 16],rax ; "saved" r13 78 | mov [rcx + 8],rax ; "saved" r14 79 | mov [rcx],rax ; "saved" r15 80 | mov [rdi],rcx 81 | ret 82 | 83 | ;=========================================================================== 84 | 85 | coroutine_yield64: 86 | _coroutine_yield64: 87 | push rbp 88 | push rbx 89 | push r12 90 | push r13 91 | push r14 92 | push r15 93 | 94 | mov rax,rsi ; value to return 95 | xchg rsp,[rdi] ; YIELD! 96 | 97 | pop r15 98 | pop r14 99 | pop r13 100 | pop r12 101 | pop rbx 102 | pop rbp 103 | ret 104 | 105 | ;*************************************************************************** 106 | -------------------------------------------------------------------------------- /iter.c: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | * 3 | * Copyright 2017 by Sean Conner. 4 | * 5 | * This library is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU Lesser General Public License as published by 7 | * the Free Software Foundation; either version 3 of the License, or (at your 8 | * option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, but 11 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 12 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 13 | * License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public License 16 | * along with this library; if not, see . 17 | * 18 | * Comments, questions and criticisms can be sent to: sean@conman.org 19 | * 20 | *************************************************************************/ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include "coroutine.h" 28 | 29 | typedef struct tree 30 | { 31 | struct tree *left; 32 | struct tree *right; 33 | int height; 34 | } tree__s; 35 | 36 | extern tree__s *tree_insert (tree__s *restrict,tree__s *restrict,int (*)(const void *,const void *)); 37 | extern tree__s *tree_find (tree__s *,const void *,int (*)(const void *,const void *)); 38 | extern tree__s *tree_remove (tree__s *,const void *,int (*)(const void *,const void *),tree__s **); 39 | 40 | static int tree_delta (tree__s *); 41 | static tree__s *tree_rotl (tree__s *); 42 | static tree__s *tree_rotr (tree__s *); 43 | static tree__s *tree_balance (tree__s *); 44 | 45 | struct word 46 | { 47 | tree__s node; 48 | char *word; 49 | }; 50 | 51 | /*************************************************************************/ 52 | 53 | static const struct word *c_tree_to_word(const tree__s *tree) 54 | { 55 | return (const struct word *)((const char *)tree - offsetof(struct word,node)); 56 | } 57 | 58 | /*************************************************************************/ 59 | 60 | static struct word *tree_to_word(tree__s *tree) 61 | { 62 | return (struct word *)((char *)tree - offsetof(struct word,node)); 63 | } 64 | 65 | /*************************************************************************/ 66 | 67 | static int wordcmp(const void *needle,const void *haystack) 68 | { 69 | const struct word *left = c_tree_to_word(needle); 70 | const struct word *right = c_tree_to_word(haystack); 71 | 72 | return strcmp(left->word,right->word); 73 | } 74 | 75 | /*************************************************************************/ 76 | 77 | static tree__s *readlist(const char *fname) 78 | { 79 | tree__s *tree = NULL; 80 | struct word *word; 81 | FILE *fp; 82 | char buffer[BUFSIZ]; 83 | size_t len; 84 | 85 | fp = (fname != NULL) ? fopen(fname,"r") : stdin; 86 | 87 | while(fgets(buffer,sizeof(buffer),fp)) 88 | { 89 | 90 | word = calloc(1,sizeof(struct word)); 91 | len = strlen(buffer) + 1; 92 | word->word = malloc(len + 1); 93 | memcpy(word->word,buffer,len); 94 | tree = tree_insert(tree,&word->node,wordcmp); 95 | } 96 | 97 | if (fp != stdin) fclose(fp); 98 | return tree; 99 | } 100 | 101 | /*************************************************************************/ 102 | 103 | static void freelist(tree__s *tree) 104 | { 105 | struct word *word; 106 | 107 | if (tree->left) freelist(tree->left); 108 | if (tree->right) freelist(tree->right); 109 | word = tree_to_word(tree); 110 | free(word->word); 111 | free(tree); 112 | } 113 | 114 | /*************************************************************************/ 115 | 116 | static uintptr_t word_iter(coroutine__s *self,uintptr_t utree) 117 | { 118 | const tree__s *tree = (const tree__s *)utree; 119 | const struct word *word; 120 | 121 | if (tree->left) word_iter(self,(uintptr_t)tree->left); 122 | 123 | word = c_tree_to_word(tree); 124 | coroutine_yield(self,(uintptr_t)word->word); 125 | 126 | if (tree->right) word_iter(self,(uintptr_t)tree->right); 127 | 128 | return 0; 129 | } 130 | 131 | /*************************************************************************/ 132 | 133 | static const char *next_word(coroutine__s **pco,tree__s *tree) 134 | { 135 | uintptr_t item; 136 | 137 | if (*pco == NULL) 138 | coroutine_create(pco,0,word_iter); 139 | 140 | item = coroutine_yield(*pco,(uintptr_t)tree); 141 | 142 | if (item == 0) 143 | { 144 | coroutine_free(*pco); 145 | *pco = NULL; 146 | } 147 | 148 | return (const char *)item; 149 | } 150 | 151 | /*************************************************************************/ 152 | 153 | static void dumpcomem(coroutine__s *co) 154 | { 155 | FILE *fp = fopen("/tmp/dump.bin","w"); 156 | fwrite(co->base,1,co->size,fp); 157 | fclose(fp); 158 | } 159 | 160 | /*************************************************************************/ 161 | 162 | int main(int argc,char *argv[]) 163 | { 164 | tree__s *list = NULL; 165 | struct word *word = NULL; 166 | coroutine__s *co = NULL; 167 | uintptr_t item; 168 | const char *w; 169 | const char *fname; 170 | 171 | fname = (argc > 1) ? argv[1] : NULL; 172 | list = readlist(fname); 173 | word = tree_to_word(list); 174 | 175 | coroutine_create(&co,0,word_iter); 176 | while((item = coroutine_yield(co,(uintptr_t)list)) != 0) 177 | printf("%s",(const char *)item); 178 | dumpcomem(co); 179 | coroutine_free(co); 180 | co = NULL; 181 | 182 | printf("\n---\n\n"); 183 | 184 | while((w = next_word(&co,list)) != NULL) 185 | printf("%s",w); 186 | 187 | freelist(list); 188 | 189 | return EXIT_SUCCESS; 190 | } 191 | 192 | /************************************************************************* 193 | * 194 | * AVL TREE IMPLEMENTATION 195 | * 196 | **************************************************************************/ 197 | 198 | static int tree_delta(tree__s *self) 199 | { 200 | return (self->left ? self->left->height : 0) 201 | - (self->right ? self->right->height : 0); 202 | } 203 | 204 | /*************************************************************************/ 205 | 206 | static tree__s *tree_rotl(tree__s *self) 207 | { 208 | tree__s *r = self->right; 209 | self->right = r->left; 210 | r->left = tree_balance(self); 211 | return tree_balance(r); 212 | } 213 | 214 | /*************************************************************************/ 215 | 216 | static tree__s *tree_rotr(tree__s *self) 217 | { 218 | tree__s *l = self->left; 219 | self->left = l->right; 220 | l->right = tree_balance(self); 221 | return tree_balance(l); 222 | } 223 | 224 | /*************************************************************************/ 225 | 226 | static tree__s *tree_balance(tree__s *self) 227 | { 228 | int delta = tree_delta(self); 229 | 230 | if (delta < -1) 231 | { 232 | if (tree_delta(self->right) > 0) 233 | self->right = tree_rotr(self->right); 234 | return tree_rotl(self); 235 | } 236 | else if (delta > 1) 237 | { 238 | if (tree_delta(self->left) < 0) 239 | self->left = tree_rotl(self->left); 240 | return tree_rotr(self); 241 | } 242 | 243 | self->height = 0; 244 | 245 | if (self->left && (self->left->height > self->height)) 246 | self->height = self->left->height; 247 | 248 | if (self->right && (self->right->height > self->height)) 249 | self->height = self->right->height; 250 | 251 | self->height += 1; 252 | return self; 253 | } 254 | 255 | /*************************************************************************/ 256 | 257 | static tree__s *tree_move_right(tree__s *restrict self,tree__s *restrict rhs) 258 | { 259 | if (self == NULL) 260 | return rhs; 261 | 262 | self->right = tree_move_right(self->right,rhs); 263 | return tree_balance(self); 264 | } 265 | 266 | /*************************************************************************/ 267 | 268 | tree__s *tree_insert( 269 | tree__s *restrict self, 270 | tree__s *restrict item, 271 | int (*compare)(const void *,const void *) 272 | ) 273 | { 274 | int rc; 275 | 276 | if (self == NULL) 277 | return item; 278 | 279 | rc = (*compare)(item,self); 280 | 281 | if (rc < 0) 282 | self->left = tree_insert(self->left,item,compare); 283 | else 284 | self->right = tree_insert(self->right,item,compare); 285 | 286 | return tree_balance(self); 287 | } 288 | 289 | /*************************************************************************/ 290 | 291 | tree__s *tree_find( 292 | tree__s *self, 293 | const void *item, 294 | int (*compare)(const void *,const void *) 295 | ) 296 | { 297 | int rc; 298 | 299 | if (self == NULL) 300 | return NULL; 301 | 302 | rc = (*compare)(item,self); 303 | 304 | if (rc < 0) 305 | return tree_find(self->left,item,compare); 306 | else if (rc == 0) 307 | return self; 308 | else 309 | return tree_find(self->right,item,compare); 310 | } 311 | 312 | /*************************************************************************/ 313 | 314 | tree__s *tree_remove( 315 | tree__s *self, 316 | const void *item, 317 | int (*compare)(const void *,const void *), 318 | tree__s **remove 319 | ) 320 | { 321 | int rc; 322 | 323 | if (self == NULL) 324 | return NULL; 325 | 326 | rc = (*compare)(item,self); 327 | 328 | if (rc < 0) 329 | self->left = tree_remove(self->left,item,compare,remove); 330 | else if (rc == 0) 331 | { 332 | if (remove != NULL) 333 | *remove = self; 334 | 335 | tree__s *tmp = tree_move_right(self->left,self->right); 336 | return tmp; 337 | } 338 | else 339 | self->right = tree_remove(self->right,item,compare,remove); 340 | 341 | return tree_balance(self); 342 | } 343 | 344 | /*************************************************************************/ 345 | 346 | -------------------------------------------------------------------------------- /megaco.c: -------------------------------------------------------------------------------- 1 | 2 | #define _GNU_SOURCE 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #ifdef __APPLE__ 11 | # error Mac OS-X does not support clock_gettime() 12 | #endif 13 | 14 | #if defined(__i386) 15 | # define coroutine_init coroutine_init32 16 | # define coroutine_yield coroutine_yield32 17 | #elif defined(__x86) 18 | # define coroutine_init coroutine_init32 19 | # define coroutine_yield coroutine_yield32 20 | #elif defined(__x86_64) 21 | # define coroutine_init coroutine_init64 22 | # define coroutine_yield coroutine_yield64 23 | #endif 24 | 25 | extern void coroutine_init (void *,uintptr_t (*)(void *,uintptr_t),void *); 26 | extern uintptr_t coroutine_yield(void *,uintptr_t); 27 | 28 | /*************************************************************************/ 29 | 30 | static void *create_co(uintptr_t (*fun)(void *,uintptr_t)) 31 | { 32 | char *blob = malloc(4192); 33 | if (blob != NULL) 34 | coroutine_init(blob,fun,&blob[4192]); 35 | return blob; 36 | } 37 | 38 | /*************************************************************************/ 39 | 40 | static uintptr_t subtask(void *self,uintptr_t data) 41 | { 42 | data = coroutine_yield(self,data); 43 | data = coroutine_yield(self,data); 44 | data = coroutine_yield(self,data); 45 | data = coroutine_yield(self,data); 46 | return data; 47 | } 48 | 49 | /*************************************************************************/ 50 | 51 | static void runtest(struct timespec *result,void *cos[],size_t max) 52 | { 53 | struct timespec start; 54 | struct timespec end; 55 | 56 | clock_gettime(CLOCK_MONOTONIC,&start); 57 | for (size_t i = 0 ; i < max ; i++) 58 | coroutine_yield(cos[i],i); 59 | clock_gettime(CLOCK_MONOTONIC,&end); 60 | 61 | if (end.tv_nsec < start.tv_nsec) 62 | { 63 | end.tv_nsec += 1000000000L; 64 | end.tv_sec--; 65 | } 66 | 67 | result->tv_nsec = end.tv_nsec - start.tv_nsec; 68 | result->tv_sec = end.tv_sec - start.tv_sec; 69 | } 70 | 71 | /*************************************************************************/ 72 | 73 | int main(int argc,char *argv[]) 74 | { 75 | struct timespec total; 76 | void **co; 77 | size_t max; 78 | 79 | if (argc == 1) 80 | max = 1000; 81 | else 82 | max = strtoul(argv[1],NULL,10); 83 | 84 | co = calloc(max,sizeof(void *)); 85 | assert(co); 86 | 87 | for (size_t i = 0 ; i < max ; i++) 88 | { 89 | co[i] = create_co(subtask); 90 | assert(co[i] != NULL); 91 | } 92 | 93 | runtest(&total,co,max); 94 | printf("Initial coroutine_yield() %ld %ld %ld\n",(long)total.tv_sec,(long)total.tv_nsec,(long)(total.tv_nsec / max)/2); 95 | 96 | runtest(&total,co,max); 97 | printf("Second coroutine_yield() %ld %ld %ld\n",(long)total.tv_sec,(long)total.tv_nsec,(long)(total.tv_nsec / max)/2); 98 | 99 | runtest(&total,co,max); 100 | printf("Third coroutine_yield() %ld %ld %ld\n",(long)total.tv_sec,(long)total.tv_nsec,(long)(total.tv_nsec / max)/2); 101 | 102 | runtest(&total,co,max); 103 | printf("Fourth coroutine_yield() %ld %ld %ld\n",(long)total.tv_sec,(long)total.tv_nsec,(long)(total.tv_nsec / max)/2); 104 | 105 | runtest(&total,co,max); 106 | printf("Final coroutine_yield() %ld %ld %ld\n",(long)total.tv_sec,(long)total.tv_nsec,(long)(total.tv_nsec / max)/2); 107 | 108 | for (size_t i = 0 ; i < max ; i++) 109 | free(co[i]); 110 | 111 | free(co); 112 | return 0; 113 | } 114 | -------------------------------------------------------------------------------- /test.c: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | * 3 | * Copyright 2017 by Sean Conner. 4 | * 5 | * This library is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU Lesser General Public License as published by 7 | * the Free Software Foundation; either version 3 of the License, or (at your 8 | * option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, but 11 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 12 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 13 | * License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public License 16 | * along with this library; if not, see . 17 | * 18 | * Comments, questions and criticisms can be sent to: sean@conman.org 19 | * 20 | *************************************************************************/ 21 | 22 | #include 23 | #include 24 | #include 25 | #include "coroutine.h" 26 | 27 | /**************************************************************************/ 28 | 29 | static uintptr_t foo(coroutine__s *self,uintptr_t d) 30 | { 31 | d = coroutine_yield(self,d); 32 | return d; 33 | } 34 | 35 | static uintptr_t routine1(coroutine__s *self,uintptr_t d) 36 | { 37 | while(true) 38 | { 39 | printf("routine1=%" PRIuPTR "\n",d); 40 | d = foo(self,d + 100); 41 | } 42 | 43 | return 0; 44 | } 45 | 46 | /**************************************************************************/ 47 | 48 | static uintptr_t routine2(coroutine__s *self,uintptr_t d) 49 | { 50 | while(true) 51 | { 52 | printf("routine2=%" PRIuPTR "\n",d); 53 | d = coroutine_yield(self,d + 1); 54 | } 55 | 56 | return 0; 57 | } 58 | 59 | /**************************************************************************/ 60 | 61 | static uintptr_t routine3(coroutine__s *self,uintptr_t d) 62 | { 63 | while(true) 64 | { 65 | printf("routine3=%" PRIuPTR "\n",d); 66 | d = coroutine_yield(self,d + 2); 67 | } 68 | 69 | return 0; 70 | } 71 | 72 | /**************************************************************************/ 73 | 74 | static uintptr_t test(coroutine__s *self,uintptr_t d) 75 | { 76 | printf("Hello %" PRIuPTR "\n",d); 77 | d = coroutine_yield(self,d + 10); 78 | printf("Goodbye %" PRIuPTR "\n",d); 79 | return d + 10; 80 | } 81 | 82 | /**************************************************************************/ 83 | 84 | int main(void) 85 | { 86 | if (true) 87 | { 88 | coroutine__s *co; 89 | uintptr_t r = 0; 90 | 91 | coroutine_create(&co,0,test); 92 | 93 | for (int i = 0 ; i < 10 ; i++) 94 | { 95 | r = coroutine_yield(co,r); 96 | printf("test-%d=%" PRIuPTR "\n",i,r); 97 | } 98 | 99 | coroutine_free(co); 100 | } 101 | 102 | printf("---------\n"); 103 | 104 | if (true) 105 | { 106 | coroutine__s *co1; 107 | coroutine__s *co2; 108 | coroutine__s *co3; 109 | 110 | coroutine_create(&co1,0,routine1); 111 | coroutine_create(&co2,0,routine2); 112 | coroutine_create(&co3,0,routine3); 113 | 114 | for (int i = 0 , r = 0 ; i < 10 ; i++) 115 | { 116 | r = coroutine_yield(co1,r); 117 | r = coroutine_yield(co2,r + 200); 118 | r = coroutine_yield(co3,r + 300); 119 | } 120 | 121 | coroutine_free(co3); 122 | coroutine_free(co2); 123 | coroutine_free(co1); 124 | } 125 | 126 | return 0; 127 | } 128 | 129 | /**************************************************************************/ 130 | --------------------------------------------------------------------------------