├── 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 |
--------------------------------------------------------------------------------