├── .gitignore ├── AUTHORS ├── HACKING ├── LICENSE ├── Makefile ├── README ├── doc ├── Makefile ├── capi.txt ├── conf.py ├── cppapi.txt ├── index.txt ├── intro.txt └── make.bat ├── examples ├── Makefile ├── simple-cc.cc └── simple.c ├── src ├── Makefile ├── greenlet ├── greenlet-asm.S ├── greenlet-cc.cc ├── greenlet-int.h ├── greenlet-sys.c ├── greenlet.c └── greenlet.h └── test ├── Makefile ├── exception.cc ├── perf-setjmp.c └── perf-switch.c /.gitignore: -------------------------------------------------------------------------------- 1 | doc/_build 2 | *.o 3 | *.a 4 | examples/simple 5 | examples/simple-cc 6 | test/exception 7 | test/perf-setjmp 8 | test/perf-switch 9 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | The following people have contributed to cgreenlet. Collectively they 2 | own the copyright of this software. 3 | 4 | * Geert Jansen : initial implementation 5 | -------------------------------------------------------------------------------- /HACKING: -------------------------------------------------------------------------------- 1 | Hacking on CGreenlet 2 | ==================== 3 | 4 | CGreenlet tries to be a FAST!, easy to use and yet simple to understand and 5 | maintain library for using coroutines. 6 | 7 | CGreenlet requires 4 platform and/or architecture specific pieces of 8 | functionality in order to be able to offer coroutines: 9 | 10 | 1. Thread-local storage. 11 | 2. Stack allocation and deallocation 12 | 3. Stack switching 13 | 4. Context switching 14 | 15 | CGreenlet's approach to portability is that a platform is only supported if it 16 | has been explicitly ported and tested. This means that it does not use 17 | autoconf to test for features, but uses the C preprocessor and Makefile 18 | conditional to see if a platform is support and adjust the build process 19 | accordingly This approach was taken because often the low-level fuctions we 20 | need may work slightly different on different platforms, or are buggy. Also 21 | this frees us from having to implement the autoconf complexity. 22 | 23 | For each platform that is supported, these 4 areas of functionality have to be 24 | provided. What follows now is an overview on how each of these are 25 | implemented: 26 | 27 | Thread-local storage 28 | -------------------- 29 | 30 | Currently two approaches are supported. The fastest and preferred way is to 31 | use a __thread allocation class keyword on thread-local variables. This is 32 | available on Linux and on Windows. This if is not available (e.g. on Mac OSX), 33 | the pthread_getspecific() API is used. 34 | 35 | Thread-local storage is implemented in "greenlet-system.c". 36 | 37 | Allocation of the stack 38 | ----------------------- 39 | 40 | On Unix-like systems, cgreenlet uses mmap() and on Windows, it uses 41 | VirtualAlloc(). The stack is protected with one PROT_NONE guard page. Stack 42 | allocation is implemented in "greenlet-system.c". 43 | 44 | Stack switching 45 | --------------- 46 | 47 | There is really no portably way to do this. On Unix-like platforms, we could 48 | use sigaltstack() + longjmp(), or the deprecated POSIX makecontext(). On 49 | Windows, the Fiber API could be used. 50 | 51 | The approach that cgreenlet has taken is to implement an architecture specific 52 | function to switch stacks: 53 | 54 | void _greenlet_callnewstack(void *stack, void *(*func)(void *), void *arg); 55 | 56 | This function will switch stack to `stack', and call `func(arg)' on the new 57 | stack. The function may not return. If it does try to return, a SIGSEGV will 58 | be raised because we overwrite the return address to NULL. The way to get out 59 | of this function is to switch context to a parent of the current context (see 60 | next section). 61 | 62 | This function is implemented in assembly in "greenlet-asm.S". 63 | 64 | Context switching 65 | ----------------- 66 | 67 | A relatively portably way to implement this would be setjmp()/longjmp(). The 68 | function is available on Unix-like platforms and Windows. However the 69 | performance of these functions varies greatly, as on some platforms this saves 70 | the signal stack and on others it doesn't. Also, setjmp()/longjmp() do not 71 | allow you to inject code just before execution is resumed in the target. Code 72 | injection is needed to propagate exceptions in C++. 73 | 74 | The approach taken by cgreenlet is to provide a fast architecture specific 75 | implementation of setjmp()/longjmp(): 76 | 77 | int _greenlet_setjmp_fast(void *frame); 78 | void _greenlet_longjmp_fast(void *frame, void (*inject_func)(void *), 79 | void *arg); 80 | 81 | These functions work just like setjmp()/longjmp() only that they are about 2x 82 | faster and allow for code injection. 83 | 84 | These functios are implemented in assemlby in "greenlet-asm.S". 85 | 86 | Contributing 87 | ============ 88 | 89 | Feel free to add an issue on the github site, or fork it and send me a merge 90 | request. Specifically patches for the ARM platform would be appreciated. 91 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 the cgreenlet authors. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | MAKE = make 2 | CFLAGS = -g -O3 -Wall -pedantic -fexceptions 3 | CXXFLAGS = -g -O3 -Wall -pedantic 4 | ARFLAGS = rv 5 | ifeq ($(shell uname), Linux) 6 | CC = gcc 7 | CXX = g++ -std=c++0x 8 | RM = rm 9 | RMFLAGS = -f 10 | PREFIX = /usr/local 11 | endif 12 | ifeq ($(shell uname), Darwin) 13 | CC = clang 14 | CXX = clang++ 15 | RM = rm 16 | RMFLAGS = -f 17 | PREFIX = /usr/local 18 | endif 19 | ifeq ($(shell uname), MINGW) 20 | CC = gcc 21 | CXX = g++ 22 | RM = del 23 | RMFLAGS = 24 | PREFIX = C:\greenlet 25 | endif 26 | 27 | ifeq ($(INCLUDE),) 28 | all: 29 | $(MAKE) -C src $@ 30 | $(MAKE) -C examples $@ 31 | $(MAKE) -C test $@ 32 | 33 | clean: 34 | $(MAKE) -C src $@ 35 | $(MAKE) -C examples $@ 36 | $(MAKE) -C test $@ 37 | endif 38 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Note 2 | ==== 3 | 4 | CGreenlet doesn't use the "stack splicing" approach that other green threads 5 | implementations use. This means that a whole stack will be allocated per 6 | greenlet. This is a lot less memory effcient than using stack splicing. 7 | 8 | You should consider this code mostly as a proof of concept. For a proven 9 | real-world implementation of green threads with stack splicing see the 10 | "stacklet" implementatin from PyPy: 11 | 12 | https://bitbucket.org/pypy/pypy/src/default/rpython/translator/c/src/stacklet 13 | 14 | The Stacklet API is a little bit more low-level. Stacklet would be an ideal 15 | backend for CGreenlet. 16 | 17 | Overview 18 | ======== 19 | 20 | CGreenlet is a FAST! and cross-platfrm coroutine library for C/C++. The API is 21 | modeled after the Python "greenlet" API [1]_. 22 | 23 | Building and Installing 24 | ======================= 25 | 26 | $ make 27 | $ sudo make install [PREFIX=prefix] 28 | 29 | Platforms 30 | ========= 31 | 32 | The list of tested platforms currently includes: 33 | 34 | * Linux (i386 and x86-64) 35 | * Mac OSX (x86-64) 36 | * Windows (i386) 37 | 38 | Comments and suggestions 39 | ======================== 40 | 41 | Freel free to add an issue on the github site: 42 | 43 | https://github.com/geertj/cgreenlet 44 | 45 | For any questions, feel free to contact me at . 46 | 47 | Documentation 48 | ============= 49 | 50 | See the docs/ and examples/ directories in the source tree. 51 | 52 | References 53 | ========== 54 | 55 | .. [1] http://pypi.python.org/pypi/greenlet 56 | -------------------------------------------------------------------------------- /doc/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = _build 9 | 10 | # Internal variables. 11 | PAPEROPT_a4 = -D latex_paper_size=a4 12 | PAPEROPT_letter = -D latex_paper_size=letter 13 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 14 | 15 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest 16 | 17 | help: 18 | @echo "Please use \`make ' where is one of" 19 | @echo " html to make standalone HTML files" 20 | @echo " dirhtml to make HTML files named index.html in directories" 21 | @echo " singlehtml to make a single large HTML file" 22 | @echo " pickle to make pickle files" 23 | @echo " json to make JSON files" 24 | @echo " htmlhelp to make HTML files and a HTML help project" 25 | @echo " qthelp to make HTML files and a qthelp project" 26 | @echo " devhelp to make HTML files and a Devhelp project" 27 | @echo " epub to make an epub" 28 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 29 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 30 | @echo " text to make text files" 31 | @echo " man to make manual pages" 32 | @echo " changes to make an overview of all changed/added/deprecated items" 33 | @echo " linkcheck to check all external links for integrity" 34 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 35 | 36 | clean: 37 | -rm -rf $(BUILDDIR)/* 38 | 39 | html: 40 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 41 | @echo 42 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 43 | 44 | dirhtml: 45 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 46 | @echo 47 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 48 | 49 | singlehtml: 50 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 51 | @echo 52 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 53 | 54 | pickle: 55 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 56 | @echo 57 | @echo "Build finished; now you can process the pickle files." 58 | 59 | json: 60 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 61 | @echo 62 | @echo "Build finished; now you can process the JSON files." 63 | 64 | htmlhelp: 65 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 66 | @echo 67 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 68 | ".hhp project file in $(BUILDDIR)/htmlhelp." 69 | 70 | qthelp: 71 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 72 | @echo 73 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 74 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 75 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/CGreenletReferenceGuide.qhcp" 76 | @echo "To view the help file:" 77 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/CGreenletReferenceGuide.qhc" 78 | 79 | devhelp: 80 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 81 | @echo 82 | @echo "Build finished." 83 | @echo "To view the help file:" 84 | @echo "# mkdir -p $$HOME/.local/share/devhelp/CGreenletReferenceGuide" 85 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/CGreenletReferenceGuide" 86 | @echo "# devhelp" 87 | 88 | epub: 89 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 90 | @echo 91 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 92 | 93 | latex: 94 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 95 | @echo 96 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 97 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 98 | "(use \`make latexpdf' here to do that automatically)." 99 | 100 | latexpdf: 101 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 102 | @echo "Running LaTeX files through pdflatex..." 103 | make -C $(BUILDDIR)/latex all-pdf 104 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 105 | 106 | text: 107 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 108 | @echo 109 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 110 | 111 | man: 112 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 113 | @echo 114 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 115 | 116 | changes: 117 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 118 | @echo 119 | @echo "The overview file is in $(BUILDDIR)/changes." 120 | 121 | linkcheck: 122 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 123 | @echo 124 | @echo "Link check complete; look for any errors in the above output " \ 125 | "or in $(BUILDDIR)/linkcheck/output.txt." 126 | 127 | doctest: 128 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 129 | @echo "Testing of doctests in the sources finished, look at the " \ 130 | "results in $(BUILDDIR)/doctest/output.txt." 131 | -------------------------------------------------------------------------------- /doc/capi.txt: -------------------------------------------------------------------------------- 1 | C API Reference 2 | =============== 3 | 4 | .. highlight:: c 5 | 6 | Greenlet type 7 | ------------- 8 | 9 | The central data structure in the C API is the ``greenlet_t``:: 10 | 11 | typedef void *(*greenet_start_func_t)(void *); 12 | 13 | typedef struct 14 | { 15 | greenlet_t *gr_parent; 16 | void *gr_stack; 17 | long gr_stacksize; 18 | int gr_flags; 19 | greenlet_start_func_t gr_start; 20 | /* private members follow */ 21 | } greenlet_t; 22 | 23 | Most functions in the cgreenlet library take a greenlet_t as their first 24 | argument. 25 | 26 | Creating greenlets 27 | ------------------ 28 | 29 | New greenlets are created using the ``greenlet_new()`` function:: 30 | 31 | greenlet_t *greenlet_new(greenlet_start_func_t start_func, 32 | greenlet_t *parent, long stacksize); 33 | 34 | The *start_func* argument specifies the greenlet's main function. The *parent* 35 | argument specifies the greenlet's parent. If *parent* is NULL this creates a 36 | greenlet that is a child of the special root greenlet. The *stacksize* 37 | argument specifies the size of the stack to allocate. If *stacksize* is 0, 38 | this allocates a stack of a platform specific default size. 39 | 40 | Switching between greenlets 41 | --------------------------- 42 | 43 | A greenlet is started with the "greenlet_switch_to()" function:: 44 | 45 | void *greenlet_switch_to(greenlet_t *greenlet, void *arg); 46 | 47 | The first time this function is called on a greenlet, a new execution context 48 | is created on the stack that was allocated by ``greenlet_new()``, and that 49 | greenlet's *start_func* method is called with *arg* as its argument. The 50 | greenlet will now be in the "STARTED" state. 51 | 52 | After the greenlet has been started, it can either return from its main 53 | routine, or switch to another greenlet. If the greenlet returns, the greenlet 54 | is marked as "DEAD", and execution switches to its parent. If the greenlet 55 | switches to another greenlet, its execution is paused at the point where the 56 | greenlet calls ``greenlet_switch_to()``. Should another greenlet switch back 57 | into this greenlet, then ``greenlet_switch_to()`` returns and resumes 58 | execution from that point. 59 | 60 | Every greenlet (except the root greenlet) has a parent. If a greenlet was 61 | created with the *parent* parameter of ``greenlet_new()`` set to NULL, the 62 | greenlet is a child of the root greenlet. 63 | 64 | When greenlets start up or switch between each other, ``void *`` pointers can 65 | be passed that allow you to pass arbitrary data. 66 | 67 | Root and current greenlets 68 | -------------------------- 69 | 70 | Each process has a special greenlet called the root greenlet. The root 71 | greenlet corresponds to the execution context that has been set up by the 72 | Operating System when the process was started. The root greenlet is retrieved 73 | using:: 74 | 75 | greenlet_t *greenlet_root(void); 76 | 77 | Each process also has exactly one current greenlet. The current greenlet is 78 | retrieved using:: 79 | 80 | greenlet_t *greenlet_current(void); 81 | 82 | A greenlet's parent is retrieved using:: 83 | 84 | greenlet_t *greenlet_parent(greenlet_t *greenlet); 85 | 86 | The only greenlet without a parent is the root greenlet. Calling 87 | ``greenlet_parent()`` for the root greenlet returns NULL. 88 | 89 | Greenlet states 90 | --------------- 91 | 92 | The state of greenlet is stored in the ``gr_flags`` member in the 93 | ``greenlet_t`` structure. It is the bitwise OR of the following values:: 94 | 95 | enum greenlet_flags 96 | { 97 | GREENLET_STARTED = 0x1, 98 | GREENLET_DEAD = 0x2 99 | }; 100 | 101 | Every greenlet except the root greenlet starts in an empty state. Once the 102 | greenlet has been switched to for the first time, it status will have the 103 | GREENLET_STARTED bit set. Once a greenlet's main function has exited, it 104 | status will have the GREENLET_DEAD bit set. The root greenlet is always in the 105 | GREENLET_STARTED status. 106 | 107 | The following two utility functions are provided to retrieve a greenlet's 108 | state:: 109 | 110 | int greenlet_isstarted(greenlet_t *greenlet); 111 | int greenlet_isdead(greenlet_t *greenlet); 112 | 113 | Terminating greenlets 114 | --------------------- 115 | 116 | Greenlets can be terminated in two ways. First, a greenlet can be reset. This 117 | destroys its execution context but keeps the stack allocated. Switching to the 118 | greenlet after it is reset would invoke the greenlet's *start_func* again from 119 | the beginning:: 120 | 121 | void greenlet_reset(greenlet_t *greenlet); 122 | 123 | The second way to terminate a greenlet is to destroy it:: 124 | 125 | void greenlet_destroy(greenlet_t *greenlet); 126 | 127 | Destroying a greenlet destroys its execution context and also deallocates its 128 | stack. The greenlet cannot be used anymore. 129 | 130 | Thread and Greenlets 131 | -------------------- 132 | 133 | Each thread in a process has its own root greenlet and child greenlets. The 134 | cgreenlet library is thread-safe in this respect. Of course you need to make 135 | sure that when you are using multiple threads, that the greenlets in different 136 | threads are properly synchronized if they access shared data. Within a single 137 | thread, synchronization is never required because there is always one and only 138 | one active greenlet. This is actually the key difference between threads and 139 | greenlets. 140 | 141 | Injecting code 142 | -------------- 143 | 144 | You can inject a function into a greenlet by using ``greenlet_inject()``:: 145 | 146 | typedef void (*greenlet_inject_func_t)(void *); 147 | 148 | void greenlet_inject(greenlet_t *greenlet, 149 | greenlet_inject_func_t inject_func); 150 | 151 | When a function has been injected this way , the injected function will be 152 | called exactly once when switching to the greenlet, just before the point 153 | where execution would normally start or resume. The argument that is passed to 154 | *inject_func* is the one that will have been passed to the greenlet as well. 155 | If the injected function returns, the greenlet execution will resume as 156 | normal. 157 | 158 | Injecting code can be useful in some special error situations or for 159 | debugging. It is also used by the C++ greenlet interface to inject exceptions 160 | from a child into a parent greenlet. 161 | -------------------------------------------------------------------------------- /doc/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # CGreenlet Reference Guide documentation build configuration file, created by 4 | # sphinx-quickstart on Mon Apr 16 20:09:51 2012. 5 | # 6 | # This file is execfile()d with the current directory set to its containing dir. 7 | # 8 | # Note that not all possible configuration values are present in this 9 | # autogenerated file. 10 | # 11 | # All configuration values have a default; values that are commented out 12 | # serve to show the default. 13 | 14 | import sys, os 15 | 16 | # If extensions (or modules to document with autodoc) are in another directory, 17 | # add these directories to sys.path here. If the directory is relative to the 18 | # documentation root, use os.path.abspath to make it absolute, like shown here. 19 | #sys.path.insert(0, os.path.abspath('.')) 20 | 21 | # -- General configuration ----------------------------------------------------- 22 | 23 | # If your documentation needs a minimal Sphinx version, state it here. 24 | #needs_sphinx = '1.0' 25 | 26 | # Add any Sphinx extension module names here, as strings. They can be extensions 27 | # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. 28 | extensions = [] 29 | 30 | # Add any paths that contain templates here, relative to this directory. 31 | templates_path = ['_templates'] 32 | 33 | # The suffix of source filenames. 34 | source_suffix = '.txt' 35 | 36 | # The encoding of source files. 37 | #source_encoding = 'utf-8-sig' 38 | 39 | # The master toctree document. 40 | master_doc = 'index' 41 | 42 | # General information about the project. 43 | project = u'CGreenlet' 44 | copyright = u'2012, Geert Jansen' 45 | 46 | # The version info for the project you're documenting, acts as replacement for 47 | # |version| and |release|, also used in various other places throughout the 48 | # built documents. 49 | # 50 | # The short X.Y version. 51 | version = '1.0' 52 | # The full version, including alpha/beta/rc tags. 53 | release = '1.0' 54 | 55 | # The language for content autogenerated by Sphinx. Refer to documentation 56 | # for a list of supported languages. 57 | #language = None 58 | 59 | # There are two options for replacing |today|: either, you set today to some 60 | # non-false value, then it is used: 61 | #today = '' 62 | # Else, today_fmt is used as the format for a strftime call. 63 | #today_fmt = '%B %d, %Y' 64 | 65 | # List of patterns, relative to source directory, that match files and 66 | # directories to ignore when looking for source files. 67 | exclude_patterns = ['_build'] 68 | 69 | # The reST default role (used for this markup: `text`) to use for all documents. 70 | #default_role = None 71 | 72 | # If true, '()' will be appended to :func: etc. cross-reference text. 73 | #add_function_parentheses = True 74 | 75 | # If true, the current module name will be prepended to all description 76 | # unit titles (such as .. function::). 77 | #add_module_names = True 78 | 79 | # If true, sectionauthor and moduleauthor directives will be shown in the 80 | # output. They are ignored by default. 81 | #show_authors = False 82 | 83 | # The name of the Pygments (syntax highlighting) style to use. 84 | pygments_style = 'sphinx' 85 | 86 | # A list of ignored prefixes for module index sorting. 87 | #modindex_common_prefix = [] 88 | 89 | 90 | # -- Options for HTML output --------------------------------------------------- 91 | 92 | # The theme to use for HTML and HTML Help pages. See the documentation for 93 | # a list of builtin themes. 94 | html_theme = 'agogo' 95 | 96 | # Theme options are theme-specific and customize the look and feel of a theme 97 | # further. For a list of options available for each theme, see the 98 | # documentation. 99 | #html_theme_options = {} 100 | 101 | # Add any paths that contain custom themes here, relative to this directory. 102 | #html_theme_path = [] 103 | 104 | # The name for this set of Sphinx documents. If None, it defaults to 105 | # " v documentation". 106 | #html_title = None 107 | 108 | # A shorter title for the navigation bar. Default is the same as html_title. 109 | #html_short_title = None 110 | 111 | # The name of an image file (relative to this directory) to place at the top 112 | # of the sidebar. 113 | #html_logo = None 114 | 115 | # The name of an image file (within the static path) to use as favicon of the 116 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 117 | # pixels large. 118 | #html_favicon = None 119 | 120 | # Add any paths that contain custom static files (such as style sheets) here, 121 | # relative to this directory. They are copied after the builtin static files, 122 | # so a file named "default.css" will overwrite the builtin "default.css". 123 | #html_static_path = ['_static'] 124 | 125 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 126 | # using the given strftime format. 127 | #html_last_updated_fmt = '%b %d, %Y' 128 | 129 | # If true, SmartyPants will be used to convert quotes and dashes to 130 | # typographically correct entities. 131 | #html_use_smartypants = True 132 | 133 | # Custom sidebar templates, maps document names to template names. 134 | #html_sidebars = {} 135 | 136 | # Additional templates that should be rendered to pages, maps page names to 137 | # template names. 138 | #html_additional_pages = {} 139 | 140 | # If false, no module index is generated. 141 | #html_domain_indices = True 142 | 143 | # If false, no index is generated. 144 | #html_use_index = True 145 | 146 | # If true, the index is split into individual pages for each letter. 147 | #html_split_index = False 148 | 149 | # If true, links to the reST sources are added to the pages. 150 | #html_show_sourcelink = True 151 | 152 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 153 | #html_show_sphinx = True 154 | 155 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 156 | #html_show_copyright = True 157 | 158 | # If true, an OpenSearch description file will be output, and all pages will 159 | # contain a tag referring to it. The value of this option must be the 160 | # base URL from which the finished HTML is served. 161 | #html_use_opensearch = '' 162 | 163 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 164 | #html_file_suffix = None 165 | 166 | # Output file base name for HTML help builder. 167 | htmlhelp_basename = 'CGreenletManual' 168 | 169 | 170 | # -- Options for LaTeX output -------------------------------------------------- 171 | 172 | # The paper size ('letter' or 'a4'). 173 | #latex_paper_size = 'letter' 174 | 175 | # The font size ('10pt', '11pt' or '12pt'). 176 | #latex_font_size = '10pt' 177 | 178 | # Grouping the document tree into LaTeX files. List of tuples 179 | # (source start file, target name, title, author, documentclass [howto/manual]). 180 | latex_documents = [ 181 | ('index', 'CGreenletManual.tex', u'CGreenlet Manual', u'Geert Jansen', 'manual'), 182 | ] 183 | 184 | # The name of an image file (relative to this directory) to place at the top of 185 | # the title page. 186 | #latex_logo = None 187 | 188 | # For "manual" documents, if this is true, then toplevel headings are parts, 189 | # not chapters. 190 | #latex_use_parts = False 191 | 192 | # If true, show page references after internal links. 193 | #latex_show_pagerefs = False 194 | 195 | # If true, show URL addresses after external links. 196 | #latex_show_urls = False 197 | 198 | # Additional stuff for the LaTeX preamble. 199 | #latex_preamble = '' 200 | 201 | # Documents to append as an appendix to all manuals. 202 | #latex_appendices = [] 203 | 204 | # If false, no module index is generated. 205 | #latex_domain_indices = True 206 | 207 | 208 | # -- Options for manual page output -------------------------------------------- 209 | 210 | # One entry per manual page. List of tuples 211 | # (source start file, name, description, authors, manual section). 212 | man_pages = [ 213 | ('intro', 'cgreenlet-intro', u'CGreenlet Intro', [u'Geert Jansen'], 1), 214 | ('capi', 'cgreenlet-capi', u'CGreenlet C API', [u'Geert Jansen'], 1), 215 | ('cppapi', 'cgreenlet-cppapi', u'CGreenlet C++ API', [u'Geert Jansen'], 1) 216 | ] 217 | -------------------------------------------------------------------------------- /doc/cppapi.txt: -------------------------------------------------------------------------------- 1 | C++ API Reference 2 | ================= 3 | 4 | .. highlight: cpp 5 | 6 | The greenlet class 7 | ------------------ 8 | 9 | The greenlet class is a wrapper around the ``greenlet_t`` structure from the 10 | cgreenlet C API. Before reading this section, make sure you have read the C 11 | API first. 12 | 13 | Two constructors and a destructor are provided for the ``greenlet`` class:: 14 | 15 | class greenlet 16 | { 17 | public: 18 | greenlet(greenlet_start_func_t start_func=0L, 19 | greenlet *parent=0L, long stacksize=OL); 20 | greenlet(greenet_t *greenlet); 21 | ~greenlet(); 22 | }; 23 | 24 | The first constructor creates a new ``greenlet`` instance from scratch. All 25 | arguments are optional and have the same measing as in the C API. The second 26 | form creates a ``greenlet`` instance from a ``greenlet_t`` struct from the C 27 | API. This allows C and C++ greenlets to exist in the same program. The 28 | destructor peforms the same thing as ``greenlet_destory()`` in the C API. 29 | 30 | Main function 31 | ------------- 32 | 33 | The greenlet's main function is always its virtual ``run()`` method:: 34 | 35 | protected: 36 | virtual void *run(void *arg); 37 | 38 | The default implementation of ``run()`` executes the *start_func* argument 39 | that was passed to the constructor. You can override ``run()`` to embed your 40 | main function in a derived class. 41 | 42 | Greenlet methods 43 | ---------------- 44 | 45 | Many functions that exist in the C API exist as method on the ``greenlet`` 46 | class in the C++ API:: 47 | 48 | public: 49 | void *switch_to(void *arg); 50 | void inject(greenlet_inject_func_t inject_func); 51 | void reset(); 52 | 53 | bool isstarted(); 54 | bool isdead(); 55 | 56 | Exceptions 57 | ---------- 58 | 59 | Exceptions that are not caught in a greenlet will exit the greenlet's main 60 | function and move it to the "DEAD" state. When this happens, the exception is 61 | then re-raised in the greenlet's parent, and the parent's parent, and so on, 62 | until either it is caught or until the root greenlet is reached. If the root 63 | greenlet does not catch the exception, the C++ program will terminate. 64 | -------------------------------------------------------------------------------- /doc/index.txt: -------------------------------------------------------------------------------- 1 | .. CGreenlet Manual documentation master file, created by 2 | sphinx-quickstart on Mon Apr 16 20:09:51 2012. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | CGreenlet Manual 7 | ================ 8 | 9 | This is the reference manual for cgreenlet. CGreenlet provides an API to work 10 | with coroutines in C and C++. 11 | 12 | Contents: 13 | 14 | .. toctree:: 15 | :maxdepth: 1 16 | 17 | intro 18 | capi 19 | cppapi 20 | -------------------------------------------------------------------------------- /doc/intro.txt: -------------------------------------------------------------------------------- 1 | Introduction 2 | ============ 3 | 4 | CGreenlet provides an API for working with coroutines in C and C++. The API 5 | is modeled after the Python *greenlet* API [1]_. 6 | 7 | For some, including me before i made an effort to understand them, coroutines 8 | are a mysterious type of function. In The Art of Computer Programming, Donald 9 | Knuth introduces coroutines as generalizations of subroutines. Instead of 10 | returning to the caller, they can also return to a different coroutine. 11 | 12 | That explanation didn't do it for me. However I think coroutines can be 13 | relatively easy explained in terms of function call stacks. When you call a 14 | function, the called function will return to you. If the function you call, 15 | calls a nested function, that nested function will first return to the 16 | function that called it, which then may return to you as the original caller. 17 | The call history is a stack. Inner functions always pop one level (or "frame") 18 | off the stack when they return to their calling function. And calling a nested 19 | function will push one level (or "frame") onto the stack. The stack idea is 20 | actually not just a visualization, it is how virtually all function call ABIs 21 | are implemented. 22 | 23 | With the idea of a function call stack in mind, coroutines can be explained as 24 | having multiple call stacks next to each other. Each coroutine corresponds to 25 | one function call stack. Normal function can only move up and down their call 26 | stack. Co-routines however, in addition to moving up and down like regular 27 | functions, can also move sideways to other stacks. Moving sideways is called 28 | "yielding" or "switching", as opposed to "calling" and "returning" wich moves 29 | up and down. Crucially, when moving sideways to another coroutine, the point 30 | of switching is remembered. And if one of the other coroutines switches back 31 | to the original coroutine, it continues exactly where it was left. 32 | 33 | In my view, that is all there is to coroutines. So why are coroutines 34 | useful? It turns out that there are a couple of use cases that are ideally 35 | suited to being solved with coroutines. Two very important ones are: 36 | 37 | 1. Producer / consumer patterns 38 | 39 | This happens for example in a scanner / parser. The scanner produces tokens 40 | from an input. The parser consumes tokens from the scanner. And both 41 | functions need context to remember where they are. 42 | 43 | Without coroutines, it is normal to implement one of the functions as a 44 | callback that saves state in some area that is preserved between function 45 | calls. However callback programming signifcanlty complicates things because 46 | the program execution is no longer sequential. With coroutines, both the 47 | producer and the consumer can be implemented as sequential functions, that 48 | switch to each other when a token is available (the scanner switches to the 49 | parser) or when a token is needed (the parser switches to the scanner). 50 | 51 | 2. Multiplexed I/O 52 | 53 | Multiplexing I/O means handling multiple streams of input and output in a 54 | single process. Traditionally, this can be done by using non-blocking I/O 55 | and select(). This has the drawback that you need to write your application 56 | as callbacks again, which greatly complicates things. Another solution is 57 | to use threads. Threads can use blocking I/O and can therefore implement 58 | sequential program logic. However threads are complicated to get right when 59 | they need to access global state. Also threads need to be managed as they 60 | can eat up system resources quickly. 61 | 62 | Co-routines are ideal for doing multiplexed I/O. They allow you to write 63 | sequential code, without having to deal with the complexities of threads. 64 | 65 | .. [1] http://pypi.python.org/pypi/greenlet 66 | -------------------------------------------------------------------------------- /doc/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | REM Command file for Sphinx documentation 4 | 5 | if "%SPHINXBUILD%" == "" ( 6 | set SPHINXBUILD=sphinx-build 7 | ) 8 | set BUILDDIR=_build 9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . 10 | if NOT "%PAPER%" == "" ( 11 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% 12 | ) 13 | 14 | if "%1" == "" goto help 15 | 16 | if "%1" == "help" ( 17 | :help 18 | echo.Please use `make ^` where ^ is one of 19 | echo. html to make standalone HTML files 20 | echo. dirhtml to make HTML files named index.html in directories 21 | echo. singlehtml to make a single large HTML file 22 | echo. pickle to make pickle files 23 | echo. json to make JSON files 24 | echo. htmlhelp to make HTML files and a HTML help project 25 | echo. qthelp to make HTML files and a qthelp project 26 | echo. devhelp to make HTML files and a Devhelp project 27 | echo. epub to make an epub 28 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter 29 | echo. text to make text files 30 | echo. man to make manual pages 31 | echo. changes to make an overview over all changed/added/deprecated items 32 | echo. linkcheck to check all external links for integrity 33 | echo. doctest to run all doctests embedded in the documentation if enabled 34 | goto end 35 | ) 36 | 37 | if "%1" == "clean" ( 38 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i 39 | del /q /s %BUILDDIR%\* 40 | goto end 41 | ) 42 | 43 | if "%1" == "html" ( 44 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html 45 | if errorlevel 1 exit /b 1 46 | echo. 47 | echo.Build finished. The HTML pages are in %BUILDDIR%/html. 48 | goto end 49 | ) 50 | 51 | if "%1" == "dirhtml" ( 52 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml 53 | if errorlevel 1 exit /b 1 54 | echo. 55 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. 56 | goto end 57 | ) 58 | 59 | if "%1" == "singlehtml" ( 60 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml 61 | if errorlevel 1 exit /b 1 62 | echo. 63 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. 64 | goto end 65 | ) 66 | 67 | if "%1" == "pickle" ( 68 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle 69 | if errorlevel 1 exit /b 1 70 | echo. 71 | echo.Build finished; now you can process the pickle files. 72 | goto end 73 | ) 74 | 75 | if "%1" == "json" ( 76 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json 77 | if errorlevel 1 exit /b 1 78 | echo. 79 | echo.Build finished; now you can process the JSON files. 80 | goto end 81 | ) 82 | 83 | if "%1" == "htmlhelp" ( 84 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp 85 | if errorlevel 1 exit /b 1 86 | echo. 87 | echo.Build finished; now you can run HTML Help Workshop with the ^ 88 | .hhp project file in %BUILDDIR%/htmlhelp. 89 | goto end 90 | ) 91 | 92 | if "%1" == "qthelp" ( 93 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp 94 | if errorlevel 1 exit /b 1 95 | echo. 96 | echo.Build finished; now you can run "qcollectiongenerator" with the ^ 97 | .qhcp project file in %BUILDDIR%/qthelp, like this: 98 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\CGreenletReferenceGuide.qhcp 99 | echo.To view the help file: 100 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\CGreenletReferenceGuide.ghc 101 | goto end 102 | ) 103 | 104 | if "%1" == "devhelp" ( 105 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp 106 | if errorlevel 1 exit /b 1 107 | echo. 108 | echo.Build finished. 109 | goto end 110 | ) 111 | 112 | if "%1" == "epub" ( 113 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub 114 | if errorlevel 1 exit /b 1 115 | echo. 116 | echo.Build finished. The epub file is in %BUILDDIR%/epub. 117 | goto end 118 | ) 119 | 120 | if "%1" == "latex" ( 121 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 122 | if errorlevel 1 exit /b 1 123 | echo. 124 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. 125 | goto end 126 | ) 127 | 128 | if "%1" == "text" ( 129 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text 130 | if errorlevel 1 exit /b 1 131 | echo. 132 | echo.Build finished. The text files are in %BUILDDIR%/text. 133 | goto end 134 | ) 135 | 136 | if "%1" == "man" ( 137 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man 138 | if errorlevel 1 exit /b 1 139 | echo. 140 | echo.Build finished. The manual pages are in %BUILDDIR%/man. 141 | goto end 142 | ) 143 | 144 | if "%1" == "changes" ( 145 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes 146 | if errorlevel 1 exit /b 1 147 | echo. 148 | echo.The overview file is in %BUILDDIR%/changes. 149 | goto end 150 | ) 151 | 152 | if "%1" == "linkcheck" ( 153 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck 154 | if errorlevel 1 exit /b 1 155 | echo. 156 | echo.Link check complete; look for any errors in the above output ^ 157 | or in %BUILDDIR%/linkcheck/output.txt. 158 | goto end 159 | ) 160 | 161 | if "%1" == "doctest" ( 162 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest 163 | if errorlevel 1 exit /b 1 164 | echo. 165 | echo.Testing of doctests in the sources finished, look at the ^ 166 | results in %BUILDDIR%/doctest/output.txt. 167 | goto end 168 | ) 169 | 170 | :end 171 | -------------------------------------------------------------------------------- /examples/Makefile: -------------------------------------------------------------------------------- 1 | INCLUDE = ../Makefile 2 | include $(INCLUDE) 3 | CFLAGS += -I../src 4 | CXXFLAGS += -I../src 5 | LDFLAGS += -L../src 6 | 7 | all: simple simple-cc 8 | 9 | simple: simple.o 10 | $(CC) $(LDFLAGS) -o $@ $^ -lgreenlet 11 | 12 | simple-cc: simple-cc.o 13 | $(CXX) $(LDFLAGS) -o $@ $^ -lgreenlet 14 | 15 | clean: 16 | $(RM) $(RMFLAGS) simple simple.o simple-cc simple-cc.o 17 | -------------------------------------------------------------------------------- /examples/simple-cc.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of cgreenlet. CGreenlet is free software available 3 | * under the terms of the MIT license. Consult the file LICENSE that was 4 | * shipped together with this source file for the exact licensing terms. 5 | * 6 | * Copyright (c) 2012 by the cgreenlet authors. See the file AUTHORS for a 7 | * full list. 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | using namespace std; 15 | 16 | greenlet *gr1; 17 | greenlet *gr2; 18 | 19 | class greenlet1: greenlet 20 | { 21 | void *run(void *arg) 22 | { 23 | cout << "in greenlet1::run()\n"; 24 | gr2->switch_to(); 25 | cout << "back in greenlet1::run()\n"; 26 | gr2->switch_to(); 27 | cout << "back again in greenlet1::run()\n"; 28 | return NULL; 29 | } 30 | }; 31 | 32 | class greenlet2: greenlet 33 | { 34 | void *run(void *arg) 35 | { 36 | cout << "in greenlet2::run()\n"; 37 | gr1->switch_to(); 38 | cout << "back in greenlet2::run()\n"; 39 | return NULL; 40 | } 41 | }; 42 | 43 | 44 | int main(int argc, char **argv) 45 | { 46 | gr1 = (greenlet *) new greenlet1; 47 | gr2 = (greenlet *) new greenlet2; 48 | 49 | cout << "entering greenlet1" << endl; 50 | if (gr1->switch_to() == gr2) 51 | cout << "back in main from greenlet2" << endl; 52 | cout << "greenlet1 dead: " << gr1->isdead() << endl; 53 | cout << "greenlet2 dead: " << gr2->isdead() << endl; 54 | cout << "re-entering greenlet1" << endl; 55 | if (gr1->switch_to() == gr1) 56 | cout << "back in main from greenet1" << endl; 57 | cout << "greenlet1 dead: " << gr1->isdead() << endl; 58 | cout << "greenlet2 dead: " << gr2->isdead() << endl; 59 | 60 | delete gr1; 61 | delete gr2; 62 | } 63 | -------------------------------------------------------------------------------- /examples/simple.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of cgreenlet. CGreenlet is free software available 3 | * under the terms of the MIT license. Consult the file LICENSE that was 4 | * shipped together with this source file for the exact licensing terms. 5 | * 6 | * Copyright (c) 2012 by the cgreenlet authors. See the file AUTHORS for a 7 | * full list. 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | 15 | greenlet_t *gr1, *gr2; 16 | 17 | 18 | void *func1(void *arg) 19 | { 20 | printf("in func1 (greenlet 1)\n"); 21 | greenlet_switch_to(gr2, NULL); 22 | printf("back in func1 (greenlet 1)\n"); 23 | greenlet_switch_to(gr2, NULL); 24 | printf("back again in func1 (greenlet 1)\n"); 25 | return NULL; 26 | } 27 | 28 | void *func2(void *arg) 29 | { 30 | printf("in func2 (greenlet 2)\n"); 31 | greenlet_switch_to(gr1, NULL); 32 | printf("back in func2 (greenlet 2)\n"); 33 | return NULL; 34 | } 35 | 36 | int main(int argc, char **argv) 37 | { 38 | gr1 = greenlet_new(func1, NULL, 0); 39 | gr2 = greenlet_new(func2, NULL, 0); 40 | 41 | if ((gr1 == NULL) || (gr2 == NULL)) 42 | { 43 | fprintf(stderr, "error: could not allocate greenlets\n"); 44 | exit(1); 45 | } 46 | 47 | printf("entering greenlet 1\n"); 48 | if (greenlet_switch_to(gr1, NULL) == gr2) 49 | printf("back in main from: greenlet 2\n"); 50 | printf("greenlet 1 dead: %d\n", greenlet_isdead(gr1)); 51 | printf("greenlet 2 dead: %d\n", greenlet_isdead(gr2)); 52 | printf("re-entering greenlet 1\n"); 53 | if (greenlet_switch_to(gr1, NULL) == gr1) 54 | printf("back in main from: greenlet 1\n"); 55 | printf("greenlet 1 dead: %d\n", greenlet_isdead(gr1)); 56 | printf("greenlet 2 dead: %d\n", greenlet_isdead(gr2)); 57 | 58 | greenlet_destroy(gr1); 59 | greenlet_destroy(gr2); 60 | 61 | return 0; 62 | } 63 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | INCLUDE = ../Makefile 2 | include $(INCLUDE) 3 | CFLAGS += -I. 4 | CXXFLAGS += -I. 5 | 6 | all: libgreenlet.a 7 | 8 | libgreenlet.a: greenlet.o greenlet-cc.o greenlet-sys.o greenlet-asm.o 9 | $(AR) $(ARFLAGS) $@ $^ 10 | 11 | install: 12 | $(INSTALL) greenlet $(PREFIX)/include 13 | $(INSTALL) greenlet.h $(PREFIX)/include 14 | $(INSTALL) libgreenlet.a $(PREFIX)/lib 15 | 16 | clean: 17 | $(RM) $(RMFLAGS) *.o libgreenlet.a 18 | -------------------------------------------------------------------------------- /src/greenlet: -------------------------------------------------------------------------------- 1 | /* vi: ft=cpp 2 | * 3 | * This file is part of cgreenlet. CGreenlet is free software available 4 | * under the terms of the MIT license. Consult the file LICENSE that was 5 | * shipped together with this source file for the exact licensing terms. 6 | * 7 | * Copyright (c) 2012 by the cgreenlet authors. See the file AUTHORS for a 8 | * full list. 9 | */ 10 | 11 | #ifndef GREENLET_INCLUDED 12 | #define GREENLET_INCLUDED 13 | 14 | #include 15 | #include 16 | 17 | 18 | struct _greenlet_data; 19 | 20 | class greenlet_exit: public std::exception {}; 21 | 22 | class greenlet 23 | { 24 | public: 25 | greenlet(greenlet_start_func_t start_func=0L, 26 | greenlet *parent=0L, int stacksize=0); 27 | greenlet(greenlet_t *greenlet); 28 | virtual ~greenlet(); 29 | 30 | static greenlet *root(); 31 | static greenlet *current(); 32 | greenlet *parent(); 33 | 34 | void *switch_to(void *arg=0L); 35 | void inject(greenlet_inject_func_t inject_func); 36 | void reset(); 37 | 38 | bool isstarted(); 39 | bool isdead(); 40 | 41 | protected: 42 | virtual void *run(void *arg); 43 | 44 | private: 45 | static void *_run(void *arg); 46 | static void _inject_exception(void *arg); 47 | 48 | greenlet_t *_greenlet; 49 | greenlet_start_func_t _start_func; 50 | _greenlet_data *_data; 51 | }; 52 | 53 | #endif /* GREENLET_INCLUDED */ 54 | -------------------------------------------------------------------------------- /src/greenlet-asm.S: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of cgreenlet. CGreenlet is free software available 3 | * under the terms of the MIT license. Consult the file LICENSE that was 4 | * shipped together with this source file for the exact licensing terms. 5 | * 6 | * Copyright (c) 2012 by the cgreenlet authors. See the file AUTHORS for a 7 | * full list. 8 | */ 9 | 10 | #if defined(__WIN32__) || defined(__APPLE__) 11 | # define cdecl(s) _##s 12 | #else 13 | # define cdecl(s) s 14 | #endif 15 | 16 | .text 17 | 18 | .globl cdecl(_greenlet_savecontext) 19 | .globl cdecl(_greenlet_switchcontext) 20 | .globl cdecl(_greenlet_newstack) 21 | 22 | #if defined(__x86_64__) 23 | 24 | cdecl(_greenlet_savecontext): 25 | .cfi_startproc 26 | .cfi_def_cfa %rsp,8 27 | movq (%rsp), %rax 28 | movq %rax, (%rdi) 29 | movq %rsp, %rax 30 | addq $0x8, %rax 31 | movq %rax, 0x8(%rdi) 32 | movq %rbp, 0x10(%rdi) 33 | movq %rbx, 0x18(%rdi) 34 | movq %r12, 0x20(%rdi) 35 | movq %r13, 0x28(%rdi) 36 | movq %r14, 0x30(%rdi) 37 | movq %r15, 0x38(%rdi) 38 | movq $0x0, %rax 39 | ret 40 | .cfi_endproc 41 | 42 | cdecl(_greenlet_switchcontext): 43 | .cfi_startproc 44 | .cfi_def_cfa %rsp,0 45 | movq 0x8(%rdi), %rsp 46 | movq 0x10(%rdi), %rbp 47 | movq 0x18(%rdi), %rbx 48 | movq 0x20(%rdi), %r12 49 | movq 0x28(%rdi), %r13 50 | movq 0x30(%rdi), %r14 51 | movq 0x38(%rdi), %r15 52 | testq %rsi, %rsi 53 | jnz 1f 54 | movq $0x1, %rax 55 | jmpq *(%rdi) 56 | 1: 57 | pushq (%rdi) 58 | .cfi_def_cfa %rsp,8 59 | movq %rdx, %rdi 60 | callq *%rsi 61 | movq $0x1, %rax 62 | ret 63 | .cfi_endproc 64 | 65 | cdecl(_greenlet_newstack): 66 | .cfi_startproc 67 | .cfi_def_cfa %rsp,8 68 | movq %rdi, %rsp 69 | movq %rdx, %rdi 70 | pushq $0x0 71 | jmpq *%rsi 72 | .cfi_endproc 73 | 74 | #elif defined(__i386__) 75 | 76 | cdecl(_greenlet_savecontext): 77 | .cfi_startproc 78 | .cfi_def_cfa %esp,8 79 | movl %edi, %eax 80 | movl 0x4(%esp), %edi 81 | movl (%esp), %ecx 82 | movl %ecx, (%edi) 83 | movl %esp, %ecx 84 | addl $0x4, %ecx 85 | movl %ecx, 0x4(%edi) 86 | movl %ebp, 0x8(%edi) 87 | movl %ebx, 0xc(%edi) 88 | movl %esi, 0x10(%edi) 89 | movl %eax, 0x14(%edi) 90 | movl %eax, %edi 91 | movl $0x0, %eax 92 | ret 93 | .cfi_endproc 94 | 95 | cdecl(_greenlet_switchcontext): 96 | .cfi_startproc 97 | .cfi_def_cfa %esp,0 98 | movl 0x4(%esp), %edi 99 | movl 0x8(%esp), %eax 100 | movl 0xc(%esp), %ecx 101 | movl (%edi), %edx 102 | movl 0x4(%edi), %esp 103 | movl 0x8(%edi), %ebp 104 | movl 0xc(%edi), %ebx 105 | movl 0x10(%edi), %esi 106 | movl 0x14(%edi), %edi 107 | testl %eax, %eax 108 | jnz .Linject 109 | movl $0x1, %eax 110 | jmpl *%edx 111 | .Linject: 112 | pushl %edx 113 | .cfi_def_cfa %esp,4 114 | pushl %ecx 115 | .cfi_def_cfa %esp,8 116 | calll *%eax 117 | popl %ecx 118 | .cfi_def_cfa %esp,4 119 | movl $0x1, %eax 120 | ret 121 | .cfi_endproc 122 | 123 | cdecl(_greenlet_newstack): 124 | .cfi_startproc 125 | .cfi_def_cfa %esp,4 126 | movl %esp, %edi 127 | movl 0x4(%edi), %esp 128 | movl 0x8(%edi), %eax 129 | movl 0xc(%edi), %ecx 130 | pushl %ecx 131 | pushl $0x0 132 | jmpl *%eax 133 | .cfi_endproc 134 | 135 | #endif 136 | -------------------------------------------------------------------------------- /src/greenlet-cc.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of cgreenlet. CGreenlet is free software available 3 | * under the terms of the MIT license. Consult the file LICENSE that was 4 | * shipped together with this source file for the exact licensing terms. 5 | * 6 | * Copyright (c) 2012 by the cgreenlet authors. See the file AUTHORS for a 7 | * full list. 8 | */ 9 | 10 | #include "greenlet" 11 | #include "greenlet-int.h" 12 | 13 | #include 14 | #include 15 | 16 | 17 | struct _greenlet_data 18 | { 19 | #ifdef HAVE_CXX11 20 | std::exception_ptr exception; 21 | #else 22 | greenlet_exit *exception; 23 | #endif 24 | }; 25 | 26 | greenlet::greenlet(greenlet_t *greenlet) 27 | { 28 | _greenlet = greenlet; 29 | _greenlet->gr_instance = this; 30 | _start_func = greenlet->gr_start; 31 | greenlet->gr_start = _run; 32 | _data = new _greenlet_data; 33 | } 34 | 35 | greenlet::greenlet(greenlet_start_func_t start_func, greenlet *parent, 36 | int stacksize) 37 | { 38 | greenlet_t *c_parent = parent ? parent->_greenlet->gr_parent : 0L; 39 | 40 | _greenlet = greenlet_new(_run, c_parent, stacksize); 41 | if (_greenlet == 0L) 42 | throw std::bad_alloc(); 43 | _greenlet->gr_instance = this; 44 | _start_func = start_func; 45 | _data = new _greenlet_data; 46 | } 47 | 48 | greenlet::~greenlet() 49 | { 50 | greenlet_destroy(_greenlet); 51 | delete _data; 52 | } 53 | 54 | greenlet *greenlet::root() 55 | { 56 | greenlet_t *c_root = greenlet_root(); 57 | greenlet *root = (greenlet *) c_root->gr_instance; 58 | if (root == 0L) 59 | root = new greenlet(c_root); 60 | return root; 61 | } 62 | 63 | greenlet *greenlet::current() 64 | { 65 | greenlet_t *c_current = greenlet_current(); 66 | greenlet *current = (greenlet *) c_current->gr_instance; 67 | if (current == 0L) 68 | current = new greenlet(c_current); 69 | return current; 70 | } 71 | 72 | greenlet *greenlet::parent() 73 | { 74 | greenlet_t *c_parent = _greenlet->gr_parent; 75 | greenlet *parent = (greenlet *) c_parent->gr_instance; 76 | if (parent == 0L) 77 | parent = new greenlet(c_parent); 78 | return parent; 79 | } 80 | 81 | void *greenlet::switch_to(void *arg) 82 | { 83 | return greenlet_switch_to(_greenlet, arg); 84 | } 85 | 86 | void greenlet::inject(greenlet_inject_func_t inject_func) 87 | { 88 | _greenlet->gr_inject = inject_func; 89 | } 90 | 91 | void greenlet::reset() 92 | { 93 | _greenlet->gr_flags = 0; 94 | } 95 | 96 | bool greenlet::isstarted() 97 | { 98 | return (_greenlet->gr_flags & GREENLET_STARTED) > 0; 99 | } 100 | 101 | bool greenlet::isdead() 102 | { 103 | return (_greenlet->gr_flags & GREENLET_DEAD) > 0; 104 | } 105 | 106 | void *greenlet::run(void *arg) 107 | { 108 | if (_start_func == 0L) 109 | return 0L; 110 | return _start_func(arg); 111 | } 112 | 113 | void *greenlet::_run(void *arg) 114 | { 115 | greenlet *greenlet = current(); 116 | 117 | try { 118 | return greenlet->run(arg); 119 | } catch (...) { 120 | greenlet = greenlet->parent(); 121 | while (greenlet->isdead()) 122 | greenlet = greenlet->parent(); 123 | greenlet->_greenlet->gr_inject = _inject_exception; 124 | #ifdef HAVE_CXX11 125 | greenlet->_data->exception = std::current_exception(); 126 | #else 127 | greenlet->_data->exception = new greenlet_exit(); 128 | #endif 129 | return 0L; 130 | } 131 | } 132 | 133 | void greenlet::_inject_exception(void *arg) 134 | { 135 | #ifdef HAVE_CXX11 136 | std::rethrow_exception(current()->_data->exception); 137 | #else 138 | throw *current()->_data->exception; 139 | #endif 140 | } 141 | -------------------------------------------------------------------------------- /src/greenlet-int.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of cgreenlet. CGreenlet is free software available 3 | * under the terms of the MIT license. Consult the file LICENSE that was 4 | * shipped together with this source file for the exact licensing terms. 5 | * 6 | * Copyright (c) 2012 by the cgreenlet authors. See the file AUTHORS for a 7 | * full list. 8 | */ 9 | 10 | #ifndef GREENLET_INT_H_INCLUDED 11 | #define GREENLET_INT_H_INCLUDED 12 | 13 | #if defined(__linux__) || defined(__WIN32__) 14 | # define TLS_USE___THREAD 15 | #elif defined(__APPLE__) 16 | # define TLS_USE_PTHREAD 17 | #else 18 | # error "Do not know how to use thread-local storage for this platform" 19 | #endif 20 | 21 | #if defined(__linux__) || defined(__APPLE__) 22 | # define STACK_USE_MMAP 23 | #elif defined(__WIN32__) 24 | # define STACK_USE_VIRTUAL_ALLOC 25 | #else 26 | # error "Do not know how to allocate stacks for this platform" 27 | #endif 28 | 29 | #if defined(__GXX_EXPERIMENTAL_CXX0X__) 30 | # define HAVE_CXX11 31 | #endif 32 | 33 | #ifdef __cplusplus 34 | extern "C" { 35 | #endif 36 | 37 | #define _greenlet_def_max_stacksize 2*1024*1024 38 | 39 | int _greenlet_savecontext(void *frame); 40 | void _greenlet_switchcontext(void *frame, void (*inject)(void *), void *arg) 41 | __attribute__((noreturn)); 42 | void _greenlet_newstack(void *stack, void (*main)(void *), void *arg) 43 | __attribute__((noreturn)); 44 | 45 | #ifdef TLS_USE_PTHREAD 46 | 47 | /* Systems without __thread a library version of these. */ 48 | greenlet_t *_greenlet_get_root(); 49 | greenlet_t *_greenlet_get_current(); 50 | void _greenlet_set_current(greenlet_t *current); 51 | 52 | #endif 53 | 54 | void *_greenlet_alloc_stack(long *size); 55 | void _greenlet_dealloc_stack(void *stack, long size); 56 | 57 | #ifdef __cplusplus 58 | } 59 | #endif 60 | 61 | #endif /* GREENLET_INT_H_INCLUDED */ 62 | -------------------------------------------------------------------------------- /src/greenlet-sys.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of cgreenlet. CGreenlet is free software available 3 | * under the terms of the MIT license. Consult the file LICENSE that was 4 | * shipped together with this source file for the exact licensing terms. 5 | * 6 | * Copyright (c) 2012 by the cgreenlet authors. See the file AUTHORS for a 7 | * full list. 8 | */ 9 | 10 | #include 11 | #include 12 | 13 | #include "greenlet.h" 14 | #include "greenlet-int.h" 15 | 16 | 17 | #ifdef TLS_USE_PTHREAD 18 | 19 | #include 20 | 21 | static pthread_once_t _tls_init_once = PTHREAD_ONCE_INIT; 22 | static pthread_key_t _root_greenlet; 23 | static pthread_key_t _current_greenlet; 24 | static int _init_complete = 0; 25 | 26 | static void _greenlet_tls_init(void) 27 | { 28 | greenlet_t *root; 29 | 30 | if (pthread_key_create(&_root_greenlet, NULL) != 0) 31 | return; 32 | if (pthread_key_create(&_current_greenlet, NULL) != 0) 33 | return; 34 | root = calloc(1, sizeof(greenlet_t)); 35 | if (root != NULL) 36 | { 37 | root->gr_flags = GREENLET_STARTED; 38 | pthread_setspecific(_root_greenlet, root); 39 | } 40 | _init_complete = 1; 41 | } 42 | 43 | greenlet_t *_greenlet_get_root() 44 | { 45 | pthread_once(&_tls_init_once, _greenlet_tls_init); 46 | if (!_init_complete) 47 | return NULL; 48 | return pthread_getspecific(_root_greenlet); 49 | } 50 | 51 | greenlet_t *_greenlet_get_current() 52 | { 53 | pthread_once(&_tls_init_once, _greenlet_tls_init); 54 | if (!_init_complete) 55 | return NULL; 56 | return pthread_getspecific(_current_greenlet); 57 | } 58 | 59 | void _greenlet_set_current(greenlet_t *current) 60 | { 61 | pthread_setspecific(_current_greenlet, current); 62 | } 63 | 64 | #endif /* TLS_USE_PTHREAD */ 65 | 66 | 67 | /* Stack allocation. */ 68 | 69 | #ifdef STACK_USE_MMAP 70 | 71 | #include 72 | #include 73 | #include 74 | 75 | void *_greenlet_alloc_stack(long *size) 76 | { 77 | long stacksize, pagesize; 78 | void *stack; 79 | struct rlimit rlim; 80 | 81 | stacksize = *size; 82 | if (stacksize == 0) { 83 | if (getrlimit(RLIMIT_STACK, &rlim) < 0) 84 | return NULL; 85 | stacksize = rlim.rlim_cur; 86 | if (stacksize > _greenlet_def_max_stacksize) 87 | stacksize = _greenlet_def_max_stacksize; 88 | } 89 | pagesize = sysconf(_SC_PAGESIZE); 90 | if ((pagesize < 0) || (stacksize < 2*pagesize)) 91 | return NULL; 92 | stacksize = (stacksize + pagesize - 1) & ~(pagesize - 1); 93 | stack = mmap(NULL, stacksize, PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0); 94 | if (stack == NULL) 95 | return NULL; 96 | if (mprotect(stack, pagesize, PROT_NONE) < 0) { 97 | munmap(stack, stacksize); 98 | return NULL; 99 | } 100 | *size = stacksize; 101 | return stack; 102 | } 103 | 104 | void _greenlet_dealloc_stack(void *stack, long size) 105 | { 106 | munmap(stack, size); 107 | } 108 | 109 | #endif /* STACK_USE_MMAP */ 110 | 111 | #ifdef STACK_USE_VIRTUAL_ALLOC 112 | 113 | #include 114 | 115 | void *_greenlet_alloc_stack(long *size) 116 | { 117 | long stacksize, pagesize; 118 | unsigned long old; 119 | void *stack; 120 | SYSTEM_INFO si; 121 | 122 | stacksize = *size; 123 | GetSystemInfo(&si); 124 | pagesize = si.dwAllocationGranularity; 125 | if ((stacksize == 0) || (stacksize > _greenlet_def_max_stacksize)) 126 | stacksize = _greenlet_def_max_stacksize; 127 | stacksize = (stacksize + pagesize - 1) & ~(pagesize - 1); 128 | stack = VirtualAlloc(NULL, stacksize, MEM_COMMIT|MEM_RESERVE, 129 | PAGE_READWRITE); 130 | if (stack == NULL) 131 | return NULL; 132 | if (VirtualProtect(stack, pagesize, PAGE_NOACCESS, &old) == 0) 133 | { 134 | VirtualFree(stack, stacksize, MEM_DECOMMIT); 135 | return NULL; 136 | } 137 | *size = stacksize; 138 | return stack; 139 | } 140 | 141 | void _greenlet_dealloc_stack(void *stack, long size) 142 | { 143 | VirtualFree(stack, size, MEM_DECOMMIT); 144 | } 145 | 146 | #endif /* STACK_USE_VIRTUAL_ALLOC */ 147 | -------------------------------------------------------------------------------- /src/greenlet.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of cgreenlet. CGreenlet is free software available 3 | * under the terms of the MIT license. Consult the file LICENSE that was 4 | * shipped together with this source file for the exact licensing terms. 5 | * 6 | * Copyright (c) 2012 by the cgreenlet authors. See the file AUTHORS for a 7 | * full list. 8 | */ 9 | 10 | #include 11 | 12 | #include "greenlet.h" 13 | #include "greenlet-int.h" 14 | 15 | 16 | /* On systems with __thread support, getting/setting the current thread is 17 | * optimized using inline functions. */ 18 | 19 | #ifdef TLS_USE___THREAD 20 | 21 | static __thread greenlet_t _root_greenlet = 22 | { NULL, NULL, 0, GREENLET_STARTED }; 23 | static __thread greenlet_t *_current_greenlet = NULL; 24 | 25 | inline greenlet_t *_greenlet_get_root() 26 | { 27 | return &_root_greenlet; 28 | } 29 | 30 | inline greenlet_t *_greenlet_get_current() 31 | { 32 | return _current_greenlet; 33 | } 34 | 35 | inline void _greenlet_set_current(greenlet_t *current) 36 | { 37 | _current_greenlet = current; 38 | } 39 | 40 | #endif /* TLS_USE___THREAD */ 41 | 42 | 43 | greenlet_t *greenlet_new(greenlet_start_func_t start_func, 44 | greenlet_t *parent, long stacksize) 45 | { 46 | greenlet_t *greenlet; 47 | 48 | greenlet = (greenlet_t *) calloc(1, sizeof(greenlet_t)); 49 | if (greenlet == NULL) 50 | return NULL; 51 | greenlet->gr_parent = parent ? parent : _greenlet_get_root(); 52 | if (greenlet->gr_parent == NULL) 53 | return NULL; 54 | greenlet->gr_start = start_func; 55 | greenlet->gr_stacksize = stacksize; 56 | greenlet->gr_stack = _greenlet_alloc_stack(&greenlet->gr_stacksize); 57 | if (greenlet->gr_stack == NULL) 58 | return NULL; 59 | return greenlet; 60 | } 61 | 62 | void greenlet_destroy(greenlet_t *greenlet) 63 | { 64 | _greenlet_dealloc_stack(greenlet->gr_stack, greenlet->gr_stacksize); 65 | } 66 | 67 | static void _greenlet_start(void *arg) 68 | { 69 | greenlet_t *greenlet = (greenlet_t *) arg; 70 | void *ret; 71 | 72 | _greenlet_set_current(greenlet); 73 | greenlet->gr_flags |= GREENLET_STARTED; 74 | if (greenlet->gr_inject) 75 | greenlet->gr_inject(greenlet->gr_arg); 76 | greenlet->gr_inject = NULL; 77 | ret = greenlet->gr_start(greenlet->gr_arg); 78 | greenlet->gr_flags |= GREENLET_DEAD; 79 | 80 | while (greenlet->gr_flags & GREENLET_DEAD) 81 | greenlet = greenlet->gr_parent; 82 | 83 | greenlet->gr_arg = ret; 84 | _greenlet_set_current(greenlet); 85 | _greenlet_switchcontext(&greenlet->gr_frame, greenlet->gr_inject, ret); 86 | } 87 | 88 | void *greenlet_switch_to(greenlet_t *greenlet, void *arg) 89 | { 90 | greenlet_t *current = greenlet_current(); 91 | 92 | if (_greenlet_savecontext(¤t->gr_frame)) 93 | { 94 | current->gr_inject = NULL; 95 | return current->gr_arg; 96 | } 97 | 98 | if (!(greenlet->gr_flags & GREENLET_STARTED)) 99 | { 100 | greenlet->gr_arg = arg; 101 | _greenlet_newstack((char *) greenlet->gr_stack + greenlet->gr_stacksize, 102 | _greenlet_start, greenlet); 103 | } 104 | 105 | while (greenlet->gr_flags & GREENLET_DEAD) 106 | greenlet = greenlet->gr_parent; 107 | 108 | greenlet->gr_arg = arg; 109 | _greenlet_set_current(greenlet); 110 | _greenlet_switchcontext(&greenlet->gr_frame, greenlet->gr_inject, arg); 111 | } 112 | 113 | void greenlet_inject(greenlet_t *greenlet, greenlet_inject_func_t inject_func) 114 | { 115 | greenlet->gr_inject = inject_func; 116 | } 117 | 118 | void greenlet_reset(greenlet_t *greenlet) 119 | { 120 | greenlet->gr_flags = 0; 121 | } 122 | 123 | greenlet_t *greenlet_root() 124 | { 125 | return _greenlet_get_root(); 126 | } 127 | 128 | greenlet_t *greenlet_current() 129 | { 130 | greenlet_t *greenlet = _greenlet_get_current(); 131 | if (greenlet == NULL) 132 | greenlet = _greenlet_get_root(); 133 | return greenlet; 134 | } 135 | 136 | greenlet_t *greenlet_parent(greenlet_t *greenlet) 137 | { 138 | return greenlet->gr_parent; 139 | } 140 | 141 | int greenlet_isstarted(greenlet_t *greenlet) 142 | { 143 | return (greenlet->gr_flags & GREENLET_STARTED) > 0; 144 | } 145 | 146 | int greenlet_isdead(greenlet_t *greenlet) 147 | { 148 | return (greenlet->gr_flags & GREENLET_DEAD) > 0; 149 | } 150 | -------------------------------------------------------------------------------- /src/greenlet.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of cgreenlet. CGreenlet is free software available 3 | * under the terms of the MIT license. Consult the file LICENSE that was 4 | * shipped together with this source file for the exact licensing terms. 5 | * 6 | * Copyright (c) 2012 by the cgreenlet authors. See the file AUTHORS for a 7 | * full list. 8 | */ 9 | 10 | #ifndef GREENLET_H_INCLUDED 11 | #define GREENLET_H_INCLUDED 12 | 13 | #ifdef __cplusplus 14 | extern "C" { 15 | #endif 16 | 17 | enum greenlet_flags 18 | { 19 | GREENLET_STARTED = 0x1, 20 | GREENLET_DEAD = 0x2 21 | }; 22 | 23 | typedef struct _greenlet_st greenlet_t; 24 | typedef void *(*greenlet_start_func_t)(void *); 25 | typedef void (*greenlet_inject_func_t)(void *); 26 | 27 | struct _greenlet_st 28 | { 29 | greenlet_t *gr_parent; 30 | void *gr_stack; 31 | long gr_stacksize; 32 | int gr_flags; 33 | greenlet_start_func_t gr_start; 34 | void *gr_arg; 35 | void *gr_instance; 36 | greenlet_inject_func_t gr_inject; 37 | void *gr_frame[8]; 38 | }; 39 | 40 | greenlet_t *greenlet_new(greenlet_start_func_t start_func, 41 | greenlet_t *parent, long stacksize); 42 | void greenlet_destroy(greenlet_t *greenlet); 43 | 44 | void *greenlet_switch_to(greenlet_t *greenlet, void *arg); 45 | void greenlet_inject(greenlet_t *greenlet, greenlet_inject_func_t inject_func); 46 | void greenlet_reset(greenlet_t *greenlet); 47 | 48 | greenlet_t *greenlet_root(); 49 | greenlet_t *greenlet_current(); 50 | greenlet_t *greenlet_parent(greenlet_t *greenlet); 51 | 52 | int greenlet_isstarted(greenlet_t *greenlet); 53 | int greenlet_isdead(greenlet_t *greenlet); 54 | 55 | #ifdef __cplusplus 56 | } 57 | #endif 58 | 59 | #endif /* GREENLET_H_INCLUDED */ 60 | -------------------------------------------------------------------------------- /test/Makefile: -------------------------------------------------------------------------------- 1 | INCLUDE = ../Makefile 2 | include $(INCLUDE) 3 | CFLAGS += -I../src 4 | CXXFLAGS += -I../src 5 | LDFLAGS += -L../src 6 | 7 | all: perf-setjmp perf-switch exception 8 | 9 | perf-setjmp: perf-setjmp.o 10 | $(CC) $(LDFLAGS) -o $@ $^ -lgreenlet -lpthread 11 | 12 | perf-switch: perf-switch.o 13 | $(CC) $(LDFLAGS) -o $@ $^ -lgreenlet -lpthread 14 | 15 | exception: exception.o 16 | $(CXX) $(LDFLAGS) -o $@ $^ -lgreenlet -lpthread 17 | 18 | clean: 19 | $(RM) $(RMFLAGS) perf-setjmp perf-switch exception *.o 20 | -------------------------------------------------------------------------------- /test/exception.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of cgreenlet. CGreenlet is free software available 3 | * under the terms of the MIT license. Consult the file LICENSE that was 4 | * shipped together with this source file for the exact licensing terms. 5 | * 6 | * Copyright (c) 2012 by the cgreenlet authors. See the file AUTHORS for a 7 | * full list. 8 | */ 9 | 10 | #include 11 | #include 12 | 13 | using namespace std; 14 | 15 | greenlet *gr1, *gr2; 16 | 17 | void *func1(void *arg) 18 | { 19 | gr2->switch_to(); 20 | return NULL; 21 | } 22 | 23 | void *func2(void *arg) 24 | { 25 | throw 10; 26 | } 27 | 28 | int main(int argc, char **argv) 29 | { 30 | gr1 = new greenlet(func1); 31 | gr2 = new greenlet(func2, gr1); 32 | 33 | try { 34 | gr1->switch_to(); 35 | } catch (int i) { 36 | cout << "Exception correctly caught in main thread." << endl; 37 | cout << "Exception value: " << i << endl; 38 | } catch (greenlet_exit &e) { 39 | cout << "Exception caught but platform does not support rethrow." << endl; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /test/perf-setjmp.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of cgreenlet. CGreenlet is free software available 3 | * under the terms of the MIT license. Consult the file LICENSE that was 4 | * shipped together with this source file for the exact licensing terms. 5 | * 6 | * Copyright (c) 2012 by the cgreenlet authors. See the file AUTHORS for a 7 | * full list. 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #include "greenlet.h" 15 | #include "greenlet-int.h" 16 | 17 | 18 | float timeit(void (*func)(int)) 19 | { 20 | int count, elapsed; 21 | struct timeval start, end; 22 | 23 | count = 1; 24 | while (1) { 25 | gettimeofday(&start, NULL); 26 | func(count); 27 | gettimeofday(&end, NULL); 28 | elapsed = end.tv_usec - start.tv_usec; 29 | elapsed += 1000000 * (end.tv_sec - start.tv_sec); 30 | if (elapsed > 1000000) 31 | break; 32 | count *= 2; 33 | } 34 | return 1000000.0 * count / elapsed; 35 | } 36 | 37 | void time_setjmp(int count) 38 | { 39 | int i; 40 | jmp_buf buf; 41 | 42 | for (i=0; i 66 | 67 | void time_getcontext(int count) 68 | { 69 | int i; 70 | volatile int ret; 71 | ucontext_t uc; 72 | 73 | for (i=0; i 11 | #include 12 | #include 13 | #include 14 | 15 | #include "greenlet.h" 16 | 17 | 18 | float timeit(void (*func)(long)) 19 | { 20 | long count, elapsed; 21 | struct timeval start, end; 22 | 23 | count = 10; 24 | while (1) { 25 | gettimeofday(&start, NULL); 26 | func(count); 27 | gettimeofday(&end, NULL); 28 | elapsed = end.tv_usec - start.tv_usec; 29 | elapsed += 1000000 * (end.tv_sec - start.tv_sec); 30 | if (elapsed > 1000000) 31 | break; 32 | count *= 2; 33 | } 34 | return 1000000.0 * count / elapsed; 35 | } 36 | 37 | volatile long counter; 38 | greenlet_t *gr1, *gr2; 39 | 40 | 41 | void *_greenlet_func1(void *arg) 42 | { 43 | while (counter > 0) 44 | greenlet_switch_to(gr2, NULL); 45 | return NULL; 46 | } 47 | 48 | void *_greenlet_func2(void *arg) 49 | { 50 | while (1) { 51 | counter--; 52 | greenlet_switch_to(gr1, NULL); 53 | } 54 | return NULL; 55 | } 56 | 57 | void time_greenlet_switch_to(long count) 58 | { 59 | gr1 = greenlet_new(_greenlet_func1, NULL, 0); 60 | gr2 = greenlet_new(_greenlet_func2, NULL, 0); 61 | if ((gr1 == NULL) || (gr2 == NULL)) { 62 | fprintf(stderr, "could not allocate greenlets\n"); 63 | exit(1); 64 | } 65 | 66 | counter = count; 67 | greenlet_switch_to(gr1, NULL); 68 | 69 | greenlet_destroy(gr1); 70 | greenlet_destroy(gr2); 71 | } 72 | 73 | pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 74 | pthread_cond_t cond = PTHREAD_COND_INITIALIZER; 75 | 76 | void *_pthread_func1(void *arg) 77 | { 78 | while (counter > 0) { 79 | pthread_mutex_lock(&mutex); 80 | pthread_cond_signal(&cond); 81 | pthread_cond_wait(&cond, &mutex); 82 | pthread_mutex_unlock(&mutex); 83 | } 84 | pthread_cond_signal(&cond); 85 | return NULL; 86 | } 87 | 88 | void *_pthread_func2(void *arg) 89 | { 90 | while (counter > 0) { 91 | pthread_mutex_lock(&mutex); 92 | counter--; 93 | pthread_cond_signal(&cond); 94 | pthread_cond_wait(&cond, &mutex); 95 | pthread_mutex_unlock(&mutex); 96 | } 97 | return NULL; 98 | } 99 | 100 | void time_pthread_cond_wait(long count) 101 | { 102 | int ret1, ret2; 103 | pthread_t pt1, pt2; 104 | void *ret; 105 | 106 | counter = count; 107 | ret1 = pthread_create(&pt1, NULL, _pthread_func1, NULL); 108 | ret2 = pthread_create(&pt2, NULL, _pthread_func2, NULL); 109 | if ((ret1 != 0) || (ret2 != 0)) { 110 | fprintf(stderr, "could not start pthreads\n"); 111 | exit(1); 112 | } 113 | 114 | pthread_join(pt1, &ret); 115 | pthread_join(pt2, &ret); 116 | } 117 | 118 | int main(int argc, char **argv) 119 | { 120 | float mps; 121 | 122 | mps = timeit(time_greenlet_switch_to) / 1000000.0; 123 | printf("greenlet_switch_to: %.2f million context switches/sec\n", mps); 124 | mps = timeit(time_pthread_cond_wait) / 1000000.0; 125 | printf("pthread_cond_wait: %.2f million context switches/sec\n", mps); 126 | 127 | return 0; 128 | } 129 | --------------------------------------------------------------------------------