pax_global_header00006660000000000000000000000064130444354020014511gustar00rootroot0000000000000052 comment=1a23cf9e011c49fe855c0afd9dcfaa6eb9c73a7e greenlet-0.4.12/000077500000000000000000000000001304443540200134025ustar00rootroot00000000000000greenlet-0.4.12/.gitignore000066400000000000000000000001731304443540200153730ustar00rootroot00000000000000*.so *.pyd *.pyc *.pyo build/ dist/ .tox/ wheelhouse/ greenlet.egg-info/ /doc/_build __pycache__/ /.ropeproject/ /MANIFEST greenlet-0.4.12/.travis.yml000066400000000000000000000002421304443540200155110ustar00rootroot00000000000000language: python sudo: false python: - 2.6 - 2.7 - 3.2 - 3.3 - 3.4 - 3.5 - 3.6 install: python setup.py build_ext -i script: python run-tests.py greenlet-0.4.12/AUTHORS000066400000000000000000000013161304443540200144530ustar00rootroot00000000000000Original Authors ---------------- * Armin Rigo * Christian Tismer Contributors ------------ * Al Stone * Alexander Schmidt * Alexey Borzenkov * Andreas Schwab * Armin Ronacher * Bin Wang * Bob Ippolito * ChangBo Guo * Christoph Gohlke * Denis Bilenko * Dirk Mueller * Donovan Preston * Fantix King * Floris Bruynooghe * Fredrik Fornwall * Gerd Woetzel * Giel van Schijndel * Gustavo Niemeyer * Guy Rozendorn * Hye-Shik Chang * Jared Kuolt * Jason Madden * Kyle Ambroff * Marc Abramowitz * Marc Schlaich * Marcin Bachry * Matt Madison * Michael Ellerman * Michael Matz * Ralf Schmitt * Robie Basak * Ronny Pfannschmidt * Samual M. Rushing * Tony Bowles * Tony Breeds * Trevor Bowen * Ulrich Weigand greenlet-0.4.12/LICENSE000066400000000000000000000026001304443540200144050ustar00rootroot00000000000000The following files are derived from Stackless Python and are subject to the same license as Stackless Python: slp_platformselect.h files in platform/ directory See LICENSE.PSF and http://www.stackless.com/ for details. Unless otherwise noted, the files in greenlet have been released under the following MIT license: Copyright (c) Armin Rigo, Christian Tismer and contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. greenlet-0.4.12/LICENSE.PSF000066400000000000000000000045701304443540200150440ustar00rootroot00000000000000PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 -------------------------------------------- 1. This LICENSE AGREEMENT is between the Python Software Foundation ("PSF"), and the Individual or Organization ("Licensee") accessing and otherwise using this software ("Python") in source or binary form and its associated documentation. 2. Subject to the terms and conditions of this License Agreement, PSF hereby grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use Python alone or in any derivative version, provided, however, that PSF's License Agreement and PSF's notice of copyright, i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Python Software Foundation; All Rights Reserved" are retained in Python alone or in any derivative version prepared by Licensee. 3. In the event Licensee prepares a derivative work that is based on or incorporates Python or any part thereof, and wants to make the derivative work available to others as provided herein, then Licensee hereby agrees to include in any such work a brief summary of the changes made to Python. 4. PSF is making Python available to Licensee on an "AS IS" basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. 5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON, OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. 6. This License Agreement will automatically terminate upon a material breach of its terms and conditions. 7. Nothing in this License Agreement shall be deemed to create any relationship of agency, partnership, or joint venture between PSF and Licensee. This License Agreement does not grant permission to use PSF trademarks or trade name in a trademark sense to endorse or promote products or services of Licensee, or any third party. 8. By copying, installing or otherwise using Python, Licensee agrees to be bound by the terms and conditions of this License Agreement. greenlet-0.4.12/MANIFEST.in000066400000000000000000000034051304443540200151420ustar00rootroot00000000000000include .travis.yml include AUTHORS include LICENSE include LICENSE.PSF include MANIFEST.in include NEWS include README.rst include appveyor.yml include appveyor/run_pip.py include appveyor/run_with_env.cmd include benchmarks/chain.py include conftest.py include dev-requirements.txt include doc/Makefile include doc/conf.py include doc/greenlet.txt include doc/index.txt include doc/make.bat include greenlet.c include greenlet.h include make-manylinux include make-win-release include my_build_ext.py include platform/switch_aarch64_gcc.h include platform/switch_amd64_unix.h include platform/switch_arm32_gcc.h include platform/switch_arm32_ios.h include platform/switch_m68k_gcc.h include platform/switch_mips_unix.h include platform/switch_ppc64_linux.h include platform/switch_ppc_aix.h include platform/switch_ppc_linux.h include platform/switch_ppc_macosx.h include platform/switch_ppc_unix.h include platform/switch_s390_unix.h include platform/switch_sparc_sun_gcc.h include platform/switch_x32_unix.h include platform/switch_x64_masm.asm include platform/switch_x64_masm.obj include platform/switch_x64_msvc.h include platform/switch_x86_msvc.h include platform/switch_x86_unix.h include run-tests.py include setup.cfg include setup.py include setup_switch_x64_masm.cmd include slp_platformselect.h include tests/__init__.py include tests/_test_extension.c include tests/_test_extension_cpp.cpp include tests/test_cpp.py include tests/test_extension_interface.py include tests/test_gc.py include tests/test_generator.py include tests/test_generator_nested.py include tests/test_greenlet.py include tests/test_leaks.py include tests/test_stack_saved.py include tests/test_throw.py include tests/test_tracing.py include tests/test_version.py include tests/test_weakref.py include tox.ini greenlet-0.4.12/NEWS000066400000000000000000000111271304443540200141030ustar00rootroot000000000000000.4.12 ====== - Stop using trashcan api 0.4.11 ====== - Fixes for aarch64 architecture 0.4.10 ====== - Added missing files to manifest - Added workaround for ppc32 on Linux - Start building binary manylinux1 wheels 0.4.9 ===== - Fixed Windows builds 0.4.8 ===== - Added support for iOS (arm32) - Added support for ppc64le 0.4.7 ===== - Added a missing workaround for `return 0` on mips - Restore compatibility with Python 2.5 - Fixed stack switching on sparc 0.4.6 ===== - Expose `_stack_saved` property on greenlet objects, it may be used to introspect the amount of memory used by a saved stack, but the API is subject to change in the future - Added a workaround for `return 0` compiler optimizations on all architectures - C API typo fixes 0.4.5 ===== - Fixed several bugs in greenlet C API - Fixed a bug in multi-threaded applications, which manifested itself with spurious "cannot switch to a different thread" exceptions - Fixed some crashes on arm and mips architectures 0.4.4 ===== - Fixed PyGreenlet_SetParent signature, thanks to BoonsNaibot - Fixed 64-bit Windows builds depending on wrong runtime dll 0.4.3 ===== - Better slp_switch performance on SPARC - Drop support for Python 2.3 - Fix trashcan assertions on debug builds of Python - Remove deprecated -fno-tree-dominator-opts compiler switch - Enable switch code for SunStudio on 32-bit SunOS - Support for abc abstract methods in greenlet subclasses - Support custom directories for tests - Document switch tracing support 0.4.2 ===== - Add .travis.yml - Fix 'err' may be used uninitialized in this function - Check _MSC_VER for msvc specific code - Fix slp_switch on SPARC for multi-threaded environments - Add support for m68k 0.4.1 ===== * fix segfaults when using gcc 4.8 on amd64/x86 unix * try to disable certain gcc 4.8 optimizations that make greenlet crash * Fix greenlet on aarch64 with gcc 4.8 * workaround segfault on SunOS/sun4v * Add support for Aarch64 * Add support for x32 psABI on x86_64 * Changed memory constraints for assembly macro for PPC Linux platforms. 0.4.0 ===== * Greenlet has an instance dictionary now, which means it can be used for implementing greenlet local storage, etc. However, this might introduce incompatibility if subclasses have __dict__ in their __slots__. Classes like that will fail, because greenlet already has __dict__ out of the box. * Greenlet no longer leaks memory after thread termination, as long as terminated thread has no running greenlets left at the time. * Add support for debian sparc and openbsd5-sparc64 * Add support for ppc64 linux * Don't allow greenlets to be copied with copy.copy/deepcopy * Fix arm32/thumb support * Restore greenlet's parent after kill * Add experimental greenlet tracing 0.3.4 ===== * Use plain distutils for install command, this fixes installation of the greenlet.h header. * Enhanced arm32 support * Fix support for Linux/S390 zSeries * Workaround compiler bug on RHEL 3 / CentOS 3 0.3.3 ===== * Use sphinx to build documentation and publish it on greenlet.rtfd.org * Prevent segfaults on openbsd 4/i386 * Workaround gcc-4.0 not allowing to clobber rbx * Enhance test infrastructure * Fix possible compilation problems when including greenlet.h in C++ mode * Make the greenlet module work on x64 windows * Add a test for greenlet C++ exceptions * Fix compilation on Solaris with SunStudio 0.3.2 ===== * Fix various crashes with recent gcc versions and VC90 * Try to fix stack save/restore on arm32 * Store and restore the threadstate on exceptions like pypy/stackless do * GreenletExit is now based on BaseException on Python >= 2.5 * Switch to using PyCapsule for Python 2.7 and 3.1 * Port for AIX on PowerPC * Fix the sparc/solaris header * Improved build dependencies patch from flub. * Can't pass parent=None to greenlet.greenlet() (fixes #21) * Rudimentary gc support (only non-live greenlets are garbage collected though) 0.3.1 ===== * Fix reference leak when passing keyword arguments to greenlets (mbachry) * Updated documentation. 0.3 === * Python 3 support. * New C API to expose Greenlets to C Extensions. * greenlet.switch() now accept's keyword arguments. * Fix Python crasher caused by switching to new greenlet from another thread. * Fix Python 2.6 crash on Windows when built with VS2009. (arigo) * arm32 support from stackless (Sylvain Baro) * Linux mips support (Thiemo Seufer) * MingGW GCC 4.4 support (Giovanni Bajo) * Fix for a threading bug (issue 40 in py lib) (arigo and ghazel) * Loads more unit tests, some from py lib (3 times as many as Greenlet 0.2) * Add documentation from py lib. * General code, documentation and repository cleanup (Kyle Ambroff, Jared Kuolt) greenlet-0.4.12/README.rst000066400000000000000000000037271304443540200151020ustar00rootroot00000000000000.. image:: https://secure.travis-ci.org/python-greenlet/greenlet.png :target: http://travis-ci.org/python-greenlet/greenlet The greenlet package is a spin-off of Stackless, a version of CPython that supports micro-threads called "tasklets". Tasklets run pseudo-concurrently (typically in a single or a few OS-level threads) and are synchronized with data exchanges on "channels". A "greenlet", on the other hand, is a still more primitive notion of micro-thread with no implicit scheduling; coroutines, in other words. This is useful when you want to control exactly when your code runs. You can build custom scheduled micro-threads on top of greenlet; however, it seems that greenlets are useful on their own as a way to make advanced control flow structures. For example, we can recreate generators; the difference with Python's own generators is that our generators can call nested functions and the nested functions can yield values too. Additionally, you don't need a "yield" keyword. See the example in tests/test_generator.py. Greenlets are provided as a C extension module for the regular unmodified interpreter. Greenlets are lightweight coroutines for in-process concurrent programming. Who is using Greenlet? ====================== There are several libraries that use Greenlet as a more flexible alternative to Python's built in coroutine support: - `Concurrence`_ - `Eventlet`_ - `Gevent`_ .. _Concurrence: http://opensource.hyves.org/concurrence/ .. _Eventlet: http://eventlet.net/ .. _Gevent: http://www.gevent.org/ Getting Greenlet ================ The easiest way to get Greenlet is to install it with pip or easy_install:: pip install greenlet easy_install greenlet Source code archives and windows installers are available on the python package index at https://pypi.python.org/pypi/greenlet The source code repository is hosted on github: https://github.com/python-greenlet/greenlet Documentation is available on readthedocs.org: https://greenlet.readthedocs.io greenlet-0.4.12/appveyor.yml000066400000000000000000000041301304443540200157700ustar00rootroot00000000000000version: 0.4.12.{build} environment: global: # SDK v7.0 MSVC Express 2008's SetEnv.cmd script will fail if the # /E:ON and /V:ON options are not enabled in the batch script intepreter # See: http://stackoverflow.com/a/13751649/163740 CMD_IN_ENV: "cmd /E:ON /V:ON /C .\\appveyor\\run_with_env.cmd" matrix: # http://www.appveyor.com/docs/installed-software#python - PYTHON_ROOT: "C:\\Python26" PYTHON_ARCH: "32" PYTHON_VERSION: "2.6.x" - PYTHON_ROOT: "C:\\Python26-x64" PYTHON_ARCH: "64" PYTHON_VERSION: "2.6.x" - PYTHON_ROOT: "C:\\Python27" PYTHON_ARCH: "32" PYTHON_VERSION: "2.7.x" - PYTHON_ROOT: "C:\\Python27-x64" PYTHON_ARCH: "64" PYTHON_VERSION: "2.7.x" - PYTHON_ROOT: "C:\\Python33" PYTHON_ARCH: "32" PYTHON_VERSION: "3.3.x" - PYTHON_ROOT: "C:\\Python33-x64" PYTHON_ARCH: "64" PYTHON_VERSION: "3.3.x" - PYTHON_ROOT: "C:\\Python34" PYTHON_ARCH: "32" PYTHON_VERSION: "3.4.x" - PYTHON_ROOT: "C:\\Python34-x64" PYTHON_ARCH: "64" PYTHON_VERSION: "3.4.x" - PYTHON_ROOT: "C:\\Python35" PYTHON_ARCH: "32" PYTHON_VERSION: "3.5.x" - PYTHON_ROOT: "C:\\Python35-x64" PYTHON_ARCH: "64" PYTHON_VERSION: "3.5.x" - PYTHON_ROOT: "C:\\Python36" PYTHON_ARCH: "32" PYTHON_VERSION: "3.6.x" - PYTHON_ROOT: "C:\\Python36-x64" PYTHON_ARCH: "64" PYTHON_VERSION: "3.6.x" install: - "SET PATH=%PYTHON_ROOT%;%PYTHON_ROOT%\\Scripts;%PATH%" - "python --version" - "python -c \"import struct; print(struct.calcsize('P') * 8)\"" # Upgrade pip to the latest version - "python appveyor\\run_pip.py install --disable-pip-version-check --upgrade pip" # Install requirements for running tests and building artifacts - "%CMD_IN_ENV% pip install --upgrade -r dev-requirements.txt" build_script: - "%CMD_IN_ENV% python setup.py build" test_script: - "%CMD_IN_ENV% python setup.py test" after_test: - "%CMD_IN_ENV% python setup.py bdist_wheel" - "%CMD_IN_ENV% python setup.py bdist_egg" artifacts: - path: dist\* greenlet-0.4.12/appveyor/000077500000000000000000000000001304443540200152475ustar00rootroot00000000000000greenlet-0.4.12/appveyor/run_pip.py000066400000000000000000000001511304443540200172720ustar00rootroot00000000000000# -*- coding: utf-8 -*- import sys from pip import main if __name__ == '__main__': sys.exit(main()) greenlet-0.4.12/appveyor/run_with_env.cmd000066400000000000000000000064461304443540200204550ustar00rootroot00000000000000:: To build extensions for 64 bit Python 3, we need to configure environment :: variables to use the MSVC 2010 C++ compilers from GRMSDKX_EN_DVD.iso of: :: MS Windows SDK for Windows 7 and .NET Framework 4 (SDK v7.1) :: :: To build extensions for 64 bit Python 2, we need to configure environment :: variables to use the MSVC 2008 C++ compilers from GRMSDKX_EN_DVD.iso of: :: MS Windows SDK for Windows 7 and .NET Framework 3.5 (SDK v7.0) :: :: 32 bit builds, and 64-bit builds for 3.5 and beyond, do not require specific :: environment configurations. :: :: Note: this script needs to be run with the /E:ON and /V:ON flags for the :: cmd interpreter, at least for (SDK v7.0) :: :: More details at: :: https://github.com/cython/cython/wiki/64BitCythonExtensionsOnWindows :: http://stackoverflow.com/a/13751649/163740 :: :: Author: Olivier Grisel :: License: CC0 1.0 Universal: http://creativecommons.org/publicdomain/zero/1.0/ :: :: Notes about batch files for Python people: :: :: Quotes in values are literally part of the values: :: SET FOO="bar" :: FOO is now five characters long: " b a r " :: If you don't want quotes, don't include them on the right-hand side. :: :: The CALL lines at the end of this file look redundant, but if you move them :: outside of the IF clauses, they do not run properly in the SET_SDK_64==Y :: case, I don't know why. @ECHO OFF SET COMMAND_TO_RUN=%* SET WIN_SDK_ROOT=C:\Program Files\Microsoft SDKs\Windows SET WIN_WDK=c:\Program Files (x86)\Windows Kits\10\Include\wdf :: Extract the major and minor versions, and allow for the minor version to be :: more than 9. This requires the version number to have two dots in it. SET MAJOR_PYTHON_VERSION=%PYTHON_VERSION:~0,1% IF "%PYTHON_VERSION:~3,1%" == "." ( SET MINOR_PYTHON_VERSION=%PYTHON_VERSION:~2,1% ) ELSE ( SET MINOR_PYTHON_VERSION=%PYTHON_VERSION:~2,2% ) :: Based on the Python version, determine what SDK version to use, and whether :: to set the SDK for 64-bit. IF %MAJOR_PYTHON_VERSION% == 2 ( SET WINDOWS_SDK_VERSION="v7.0" SET SET_SDK_64=Y ) ELSE ( IF %MAJOR_PYTHON_VERSION% == 3 ( SET WINDOWS_SDK_VERSION="v7.1" IF %MINOR_PYTHON_VERSION% LEQ 4 ( SET SET_SDK_64=Y ) ELSE ( SET SET_SDK_64=N IF EXIST "%WIN_WDK%" ( :: See: https://connect.microsoft.com/VisualStudio/feedback/details/1610302/ REN "%WIN_WDK%" 0wdf ) ) ) ELSE ( ECHO Unsupported Python version: "%MAJOR_PYTHON_VERSION%" EXIT 1 ) ) IF %PYTHON_ARCH% == 64 ( IF %SET_SDK_64% == Y ( ECHO Configuring Windows SDK %WINDOWS_SDK_VERSION% for Python %MAJOR_PYTHON_VERSION% on a 64 bit architecture SET DISTUTILS_USE_SDK=1 SET MSSdk=1 "%WIN_SDK_ROOT%\%WINDOWS_SDK_VERSION%\Setup\WindowsSdkVer.exe" -q -version:%WINDOWS_SDK_VERSION% "%WIN_SDK_ROOT%\%WINDOWS_SDK_VERSION%\Bin\SetEnv.cmd" /x64 /release ECHO Executing: %COMMAND_TO_RUN% call %COMMAND_TO_RUN% || EXIT 1 ) ELSE ( ECHO Using default MSVC build environment for 64 bit architecture ECHO Executing: %COMMAND_TO_RUN% call %COMMAND_TO_RUN% || EXIT 1 ) ) ELSE ( ECHO Using default MSVC build environment for 32 bit architecture ECHO Executing: %COMMAND_TO_RUN% call %COMMAND_TO_RUN% || EXIT 1 ) greenlet-0.4.12/benchmarks/000077500000000000000000000000001304443540200155175ustar00rootroot00000000000000greenlet-0.4.12/benchmarks/chain.py000077500000000000000000000020201304443540200171500ustar00rootroot00000000000000#!/usr/bin/env python """Create a chain of coroutines and pass a value from one end to the other, where each coroutine will increment the value before passing it along. """ import optparse import time import greenlet def link(next_greenlet): value = greenlet.getcurrent().parent.switch() next_greenlet.switch(value + 1) def chain(n): start_node = greenlet.getcurrent() for i in xrange(n): g = greenlet.greenlet(link) g.switch(start_node) start_node = g return start_node.switch(0) if __name__ == '__main__': p = optparse.OptionParser( usage='%prog [-n NUM_COROUTINES]', description=__doc__) p.add_option( '-n', type='int', dest='num_greenlets', default=100000, help='The number of greenlets in the chain.') options, args = p.parse_args() if len(args) != 0: p.error('unexpected arguments: %s' % ', '.join(args)) start_time = time.clock() print 'Result:', chain(options.num_greenlets) print time.clock() - start_time, 'seconds' greenlet-0.4.12/conftest.py000066400000000000000000000007151304443540200156040ustar00rootroot00000000000000# configuration file for py.test import sys, os from distutils.spawn import spawn def pytest_configure(config): os.chdir(os.path.dirname(__file__)) cmd = [sys.executable, "setup.py", "-q", "build_ext", "-q", "-i"] spawn(cmd, search_path=0) from tests import build_test_extensions build_test_extensions() def pytest_report_header(config): import greenlet return "greenlet %s from %s" % (greenlet.__version__, greenlet.__file__) greenlet-0.4.12/dev-requirements.txt000066400000000000000000000000211304443540200174330ustar00rootroot00000000000000setuptools wheel greenlet-0.4.12/doc/000077500000000000000000000000001304443540200141475ustar00rootroot00000000000000greenlet-0.4.12/doc/Makefile000066400000000000000000000127041304443540200156130ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/greenlet.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/greenlet.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/greenlet" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/greenlet" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." greenlet-0.4.12/doc/conf.py000066400000000000000000000172021304443540200154500ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # greenlet documentation build configuration file, created by # sphinx-quickstart on Tue Dec 27 22:58:53 2011. # # This file is execfile()d with the current directory set to its containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys, os # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. #sys.path.insert(0, os.path.abspath('.')) # -- General configuration ----------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [] # Add any paths that contain templates here, relative to this directory. templates_path = [] # ['_templates'] # The suffix of source filenames. source_suffix = '.txt' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'greenlet' copyright = u'2011, Armin Rigo, Christian Tismer' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = '0.4' # The full version, including alpha/beta/rc tags. release = '0.4.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build'] # The reST default role (used for this markup: `text`) to use for all documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'default' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = [] # ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'greenletdoc' # -- Options for LaTeX output -------------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). #'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). #'pointsize': '10pt', # Additional stuff for the LaTeX preamble. #'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ('index', 'greenlet.tex', u'greenlet Documentation', u'Armin Rigo, Christian Tismer', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output -------------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'greenlet', u'greenlet Documentation', [u'Armin Rigo, Christian Tismer'], 1) ] # If true, show URL addresses after external links. #man_show_urls = False # -- Options for Texinfo output ------------------------------------------------ # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', 'greenlet', u'greenlet Documentation', u'Armin Rigo, Christian Tismer', 'greenlet', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. #texinfo_appendices = [] # If false, no module index is generated. #texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. #texinfo_show_urls = 'footnote' greenlet-0.4.12/doc/greenlet.txt000066400000000000000000000413301304443540200165160ustar00rootroot00000000000000===================================================== greenlet: Lightweight concurrent programming ===================================================== .. toctree:: :maxdepth: 2 Motivation ========== The "greenlet" package is a spin-off of `Stackless`_, a version of CPython that supports micro-threads called "tasklets". Tasklets run pseudo-concurrently (typically in a single or a few OS-level threads) and are synchronized with data exchanges on "channels". A "greenlet", on the other hand, is a still more primitive notion of micro-thread with no implicit scheduling; coroutines, in other words. This is useful when you want to control exactly when your code runs. You can build custom scheduled micro-threads on top of greenlet; however, it seems that greenlets are useful on their own as a way to make advanced control flow structures. For example, we can recreate generators; the difference with Python's own generators is that our generators can call nested functions and the nested functions can yield values too. (Additionally, you don't need a "yield" keyword. See the example in ``test/test_generator.py``). Greenlets are provided as a C extension module for the regular unmodified interpreter. .. _`Stackless`: http://www.stackless.com Example ------- Let's consider a system controlled by a terminal-like console, where the user types commands. Assume that the input comes character by character. In such a system, there will typically be a loop like the following one:: def process_commands(*args): while True: line = '' while not line.endswith('\n'): line += read_next_char() if line == 'quit\n': print "are you sure?" if read_next_char() != 'y': continue # ignore the command process_command(line) Now assume that you want to plug this program into a GUI. Most GUI toolkits are event-based. They will invoke a call-back for each character the user presses. [Replace "GUI" with "XML expat parser" if that rings more bells to you ``:-)``] In this setting, it is difficult to implement the read_next_char() function needed by the code above. We have two incompatible functions:: def event_keydown(key): ?? def read_next_char(): ?? should wait for the next event_keydown() call You might consider doing that with threads. Greenlets are an alternate solution that don't have the related locking and shutdown problems. You start the process_commands() function in its own, separate greenlet, and then you exchange the keypresses with it as follows:: def event_keydown(key): # jump into g_processor, sending it the key g_processor.switch(key) def read_next_char(): # g_self is g_processor in this simple example g_self = greenlet.getcurrent() # jump to the parent (main) greenlet, waiting for the next key next_char = g_self.parent.switch() return next_char g_processor = greenlet(process_commands) g_processor.switch(*args) # input arguments to process_commands() gui.mainloop() In this example, the execution flow is: when read_next_char() is called, it is part of the g_processor greenlet, so when it switches to its parent greenlet, it resumes execution in the top-level main loop (the GUI). When the GUI calls event_keydown(), it switches to g_processor, which means that the execution jumps back wherever it was suspended in that greenlet -- in this case, to the switch() instruction in read_next_char() -- and the ``key`` argument in event_keydown() is passed as the return value of the switch() in read_next_char(). Note that read_next_char() will be suspended and resumed with its call stack preserved, so that it will itself return to different positions in process_commands() depending on where it was originally called from. This allows the logic of the program to be kept in a nice control-flow way; we don't have to completely rewrite process_commands() to turn it into a state machine. Usage ===== Introduction ------------ A "greenlet" is a small independent pseudo-thread. Think about it as a small stack of frames; the outermost (bottom) frame is the initial function you called, and the innermost frame is the one in which the greenlet is currently paused. You work with greenlets by creating a number of such stacks and jumping execution between them. Jumps are never implicit: a greenlet must choose to jump to another greenlet, which will cause the former to suspend and the latter to resume where it was suspended. Jumping between greenlets is called "switching". When you create a greenlet, it gets an initially empty stack; when you first switch to it, it starts to run a specified function, which may call other functions, switch out of the greenlet, etc. When eventually the outermost function finishes its execution, the greenlet's stack becomes empty again and the greenlet is "dead". Greenlets can also die of an uncaught exception. For example:: from greenlet import greenlet def test1(): print 12 gr2.switch() print 34 def test2(): print 56 gr1.switch() print 78 gr1 = greenlet(test1) gr2 = greenlet(test2) gr1.switch() The last line jumps to test1, which prints 12, jumps to test2, prints 56, jumps back into test1, prints 34; and then test1 finishes and gr1 dies. At this point, the execution comes back to the original ``gr1.switch()`` call. Note that 78 is never printed. Parents ------- Let's see where execution goes when a greenlet dies. Every greenlet has a "parent" greenlet. The parent greenlet is initially the one in which the greenlet was created (this can be changed at any time). The parent is where execution continues when a greenlet dies. This way, greenlets are organized in a tree. Top-level code that doesn't run in a user-created greenlet runs in the implicit "main" greenlet, which is the root of the tree. In the above example, both gr1 and gr2 have the main greenlet as a parent. Whenever one of them dies, the execution comes back to "main". Uncaught exceptions are propagated into the parent, too. For example, if the above test2() contained a typo, it would generate a NameError that would kill gr2, and the exception would go back directly into "main". The traceback would show test2, but not test1. Remember, switches are not calls, but transfer of execution between parallel "stack containers", and the "parent" defines which stack logically comes "below" the current one. Instantiation ------------- ``greenlet.greenlet`` is the greenlet type, which supports the following operations: ``greenlet(run=None, parent=None)`` Create a new greenlet object (without running it). ``run`` is the callable to invoke, and ``parent`` is the parent greenlet, which defaults to the current greenlet. ``greenlet.getcurrent()`` Returns the current greenlet (i.e. the one which called this function). ``greenlet.GreenletExit`` This special exception does not propagate to the parent greenlet; it can be used to kill a single greenlet. The ``greenlet`` type can be subclassed, too. A greenlet runs by calling its ``run`` attribute, which is normally set when the greenlet is created; but for subclasses it also makes sense to define a ``run`` method instead of giving a ``run`` argument to the constructor. Switching --------- Switches between greenlets occur when the method switch() of a greenlet is called, in which case execution jumps to the greenlet whose switch() is called, or when a greenlet dies, in which case execution jumps to the parent greenlet. During a switch, an object or an exception is "sent" to the target greenlet; this can be used as a convenient way to pass information between greenlets. For example:: def test1(x, y): z = gr2.switch(x+y) print z def test2(u): print u gr1.switch(42) gr1 = greenlet(test1) gr2 = greenlet(test2) gr1.switch("hello", " world") This prints "hello world" and 42, with the same order of execution as the previous example. Note that the arguments of test1() and test2() are not provided when the greenlet is created, but only the first time someone switches to it. Here are the precise rules for sending objects around: ``g.switch(*args, **kwargs)`` Switches execution to the greenlet ``g``, sending it the given arguments. As a special case, if ``g`` did not start yet, then it will start to run now. Dying greenlet If a greenlet's ``run()`` finishes, its return value is the object sent to its parent. If ``run()`` terminates with an exception, the exception is propagated to its parent (unless it is a ``greenlet.GreenletExit`` exception, in which case the exception object is caught and *returned* to the parent). Apart from the cases described above, the target greenlet normally receives the object as the return value of the call to ``switch()`` in which it was previously suspended. Indeed, although a call to ``switch()`` does not return immediately, it will still return at some point in the future, when some other greenlet switches back. When this occurs, then execution resumes just after the ``switch()`` where it was suspended, and the ``switch()`` itself appears to return the object that was just sent. This means that ``x = g.switch(y)`` will send the object ``y`` to ``g``, and will later put the (unrelated) object that some (unrelated) greenlet passes back to us into ``x``. Note that any attempt to switch to a dead greenlet actually goes to the dead greenlet's parent, or its parent's parent, and so on. (The final parent is the "main" greenlet, which is never dead.) Methods and attributes of greenlets ----------------------------------- ``g.switch(*args, **kwargs)`` Switches execution to the greenlet ``g``. See above. ``g.run`` The callable that ``g`` will run when it starts. After ``g`` started, this attribute no longer exists. ``g.parent`` The parent greenlet. This is writeable, but it is not allowed to create cycles of parents. ``g.gr_frame`` The current top frame, or None. ``g.dead`` True if ``g`` is dead (i.e. it finished its execution). ``bool(g)`` True if ``g`` is active, False if it is dead or not yet started. ``g.throw([typ, [val, [tb]]])`` Switches execution to the greenlet ``g``, but immediately raises the given exception in ``g``. If no argument is provided, the exception defaults to ``greenlet.GreenletExit``. The normal exception propagation rules apply, as described above. Note that calling this method is almost equivalent to the following:: def raiser(): raise typ, val, tb g_raiser = greenlet(raiser, parent=g) g_raiser.switch() except that this trick does not work for the ``greenlet.GreenletExit`` exception, which would not propagate from ``g_raiser`` to ``g``. Greenlets and Python threads ---------------------------- Greenlets can be combined with Python threads; in this case, each thread contains an independent "main" greenlet with a tree of sub-greenlets. It is not possible to mix or switch between greenlets belonging to different threads. Garbage-collecting live greenlets --------------------------------- If all the references to a greenlet object go away (including the references from the parent attribute of other greenlets), then there is no way to ever switch back to this greenlet. In this case, a GreenletExit exception is generated into the greenlet. This is the only case where a greenlet receives the execution asynchronously. This gives ``try:finally:`` blocks a chance to clean up resources held by the greenlet. This feature also enables a programming style in which greenlets are infinite loops waiting for data and processing it. Such loops are automatically interrupted when the last reference to the greenlet goes away. The greenlet is expected to either die or be resurrected by having a new reference to it stored somewhere; just catching and ignoring the GreenletExit is likely to lead to an infinite loop. Greenlets do not participate in garbage collection; cycles involving data that is present in a greenlet's frames will not be detected. Storing references to other greenlets cyclically may lead to leaks. Tracing support --------------- Standard Python tracing and profiling doesn't work as expected when used with greenlet since stack and frame switching happens on the same Python thread. It is difficult to detect greenlet switching reliably with conventional methods, so to improve support for debugging, tracing and profiling greenlet based code there are new functions in the greenlet module: ``greenlet.gettrace()`` Returns a previously set tracing function, or None. ``greenlet.settrace(callback)`` Sets a new tracing function and returns a previous tracing function, or None. The callback is called on various events and is expected to have the following signature:: def callback(event, args): if event == 'switch': origin, target = args # Handle a switch from origin to target. # Note that callback is running in the context of target # greenlet and any exceptions will be passed as if # target.throw() was used instead of a switch. return if event == 'throw': origin, target = args # Handle a throw from origin to target. # Note that callback is running in the context of target # greenlet and any exceptions will replace the original, as # if target.throw() was used with the replacing exception. return For compatibility it is very important to unpack args tuple only when event is either ``'switch'`` or ``'throw'`` and not when ``event`` is potentially something else. This way API can be extended to new events similar to ``sys.settrace()``. C API Reference =============== Greenlets can be created and manipulated from extension modules written in C or C++, or from applications that embed Python. The ``greenlet.h`` header is provided, and exposes the entire API available to pure Python modules. Types ----- +--------------------+-------------------+ | Type name | Python name | +====================+===================+ | PyGreenlet | greenlet.greenlet | +--------------------+-------------------+ Exceptions ---------- +---------------------+-----------------------+ | Type name | Python name | +=====================+=======================+ | PyExc_GreenletError | greenlet.error | +---------------------+-----------------------+ | PyExc_GreenletExit | greenlet.GreenletExit | +---------------------+-----------------------+ Reference --------- ``PyGreenlet_Import()`` A macro that imports the greenlet module and initializes the C API. This must be called once for each extension module that uses the greenlet C API. ``int PyGreenlet_Check(PyObject *p)`` Macro that returns true if the argument is a PyGreenlet. ``int PyGreenlet_STARTED(PyGreenlet *g)`` Macro that returns true if the greenlet ``g`` has started. ``int PyGreenlet_ACTIVE(PyGreenlet *g)`` Macro that returns true if the greenlet ``g`` has started and has not died. ``PyGreenlet *PyGreenlet_GET_PARENT(PyGreenlet *g)`` Macro that returns the parent greenlet of ``g``. ``int PyGreenlet_SetParent(PyGreenlet *g, PyGreenlet *nparent)`` Set the parent greenlet of ``g``. Returns 0 for success. If -1 is returned, then ``g`` is not a pointer to a PyGreenlet, and an AttributeError will be raised. ``PyGreenlet *PyGreenlet_GetCurrent(void)`` Returns the currently active greenlet object. ``PyGreenlet *PyGreenlet_New(PyObject *run, PyObject *parent)`` Creates a new greenlet object with the callable ``run`` and parent ``parent``. Both parameters are optional. If ``run`` is NULL, then the greenlet will be created, but will fail if switched in. If ``parent`` is NULL, the parent is automatically set to the current greenlet. ``PyObject *PyGreenlet_Switch(PyGreenlet *g, PyObject *args, PyObject *kwargs)`` Switches to the greenlet ``g``. ``args`` and ``kwargs`` are optional and can be NULL. If ``args`` is NULL, an empty tuple is passed to the target greenlet. If kwargs is NULL, no keyword arguments are passed to the target greenlet. If arguments are specified, ``args`` should be a tuple and ``kwargs`` should be a dict. ``PyObject *PyGreenlet_Throw(PyGreenlet *g, PyObject *typ, PyObject *val, PyObject *tb)`` Switches to greenlet ``g``, but immediately raise an exception of type ``typ`` with the value ``val``, and optionally, the traceback object ``tb``. ``tb`` can be NULL. Indices and tables ================== * :ref:`search` greenlet-0.4.12/doc/index.txt000066400000000000000000000000321304443540200160120ustar00rootroot00000000000000.. include:: greenlet.txt greenlet-0.4.12/doc/make.bat000066400000000000000000000117541304443540200155640ustar00rootroot00000000000000@ECHO OFF REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set BUILDDIR=_build set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . set I18NSPHINXOPTS=%SPHINXOPTS% . if NOT "%PAPER%" == "" ( set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% ) if "%1" == "" goto help if "%1" == "help" ( :help echo.Please use `make ^` where ^ is one of echo. html to make standalone HTML files echo. dirhtml to make HTML files named index.html in directories echo. singlehtml to make a single large HTML file echo. pickle to make pickle files echo. json to make JSON files echo. htmlhelp to make HTML files and a HTML help project echo. qthelp to make HTML files and a qthelp project echo. devhelp to make HTML files and a Devhelp project echo. epub to make an epub echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter echo. text to make text files echo. man to make manual pages echo. texinfo to make Texinfo files echo. gettext to make PO message catalogs echo. changes to make an overview over all changed/added/deprecated items echo. linkcheck to check all external links for integrity echo. doctest to run all doctests embedded in the documentation if enabled goto end ) if "%1" == "clean" ( for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i del /q /s %BUILDDIR%\* goto end ) if "%1" == "html" ( %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/html. goto end ) if "%1" == "dirhtml" ( %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. goto end ) if "%1" == "singlehtml" ( %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. goto end ) if "%1" == "pickle" ( %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the pickle files. goto end ) if "%1" == "json" ( %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the JSON files. goto end ) if "%1" == "htmlhelp" ( %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run HTML Help Workshop with the ^ .hhp project file in %BUILDDIR%/htmlhelp. goto end ) if "%1" == "qthelp" ( %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: echo.^> qcollectiongenerator %BUILDDIR%\qthelp\greenlet.qhcp echo.To view the help file: echo.^> assistant -collectionFile %BUILDDIR%\qthelp\greenlet.ghc goto end ) if "%1" == "devhelp" ( %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp if errorlevel 1 exit /b 1 echo. echo.Build finished. goto end ) if "%1" == "epub" ( %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub if errorlevel 1 exit /b 1 echo. echo.Build finished. The epub file is in %BUILDDIR%/epub. goto end ) if "%1" == "latex" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex if errorlevel 1 exit /b 1 echo. echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. goto end ) if "%1" == "text" ( %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text if errorlevel 1 exit /b 1 echo. echo.Build finished. The text files are in %BUILDDIR%/text. goto end ) if "%1" == "man" ( %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man if errorlevel 1 exit /b 1 echo. echo.Build finished. The manual pages are in %BUILDDIR%/man. goto end ) if "%1" == "texinfo" ( %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo if errorlevel 1 exit /b 1 echo. echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. goto end ) if "%1" == "gettext" ( %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale if errorlevel 1 exit /b 1 echo. echo.Build finished. The message catalogs are in %BUILDDIR%/locale. goto end ) if "%1" == "changes" ( %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes if errorlevel 1 exit /b 1 echo. echo.The overview file is in %BUILDDIR%/changes. goto end ) if "%1" == "linkcheck" ( %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck if errorlevel 1 exit /b 1 echo. echo.Link check complete; look for any errors in the above output ^ or in %BUILDDIR%/linkcheck/output.txt. goto end ) if "%1" == "doctest" ( %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest if errorlevel 1 exit /b 1 echo. echo.Testing of doctests in the sources finished, look at the ^ results in %BUILDDIR%/doctest/output.txt. goto end ) :end greenlet-0.4.12/greenlet.c000066400000000000000000001353401304443540200153610ustar00rootroot00000000000000/* vim:set noet ts=8 sw=8 : */ #define GREENLET_MODULE #include "greenlet.h" #include "structmember.h" /*********************************************************** A PyGreenlet is a range of C stack addresses that must be saved and restored in such a way that the full range of the stack contains valid data when we switch to it. Stack layout for a greenlet: | ^^^ | | older data | | | stack_stop . |_______________| . | | . | greenlet data | . | in stack | . * |_______________| . . _____________ stack_copy + stack_saved . | | | | . | data | |greenlet data| . | unrelated | | saved | . | to | | in heap | stack_start . | this | . . |_____________| stack_copy | greenlet | | | | newer data | | vvv | Note that a greenlet's stack data is typically partly at its correct place in the stack, and partly saved away in the heap, but always in the above configuration: two blocks, the more recent one in the heap and the older one still in the stack (either block may be empty). Greenlets are chained: each points to the previous greenlet, which is the one that owns the data currently in the C stack above my stack_stop. The currently running greenlet is the first element of this chain. The main (initial) greenlet is the last one. Greenlets whose stack is entirely in the heap can be skipped from the chain. The chain is not related to execution order, but only to the order in which bits of C stack happen to belong to greenlets at a particular point in time. The main greenlet doesn't have a stack_stop: it is responsible for the complete rest of the C stack, and we don't know where it begins. We use (char*) -1, the largest possible address. States: stack_stop == NULL && stack_start == NULL: did not start yet stack_stop != NULL && stack_start == NULL: already finished stack_stop != NULL && stack_start != NULL: active The running greenlet's stack_start is undefined but not NULL. ***********************************************************/ /*** global state ***/ /* In the presence of multithreading, this is a bit tricky: - ts_current always store a reference to a greenlet, but it is not really the current greenlet after a thread switch occurred. - each *running* greenlet uses its run_info field to know which thread it is attached to. A greenlet can only run in the thread where it was created. This run_info is a ref to tstate->dict. - the thread state dict is used to save and restore ts_current, using the dictionary key 'ts_curkey'. */ /* Python <= 2.5 support */ #if PY_MAJOR_VERSION < 3 #ifndef Py_REFCNT # define Py_REFCNT(ob) (((PyObject *) (ob))->ob_refcnt) #endif #ifndef Py_TYPE # define Py_TYPE(ob) (((PyObject *) (ob))->ob_type) #endif #ifndef PyVarObject_HEAD_INIT # define PyVarObject_HEAD_INIT(type, size) \ PyObject_HEAD_INIT(type) size, #endif #endif #if PY_VERSION_HEX < 0x02060000 #define PyLong_FromSsize_t PyInt_FromLong #endif #if PY_VERSION_HEX < 0x02050000 typedef int Py_ssize_t; #endif extern PyTypeObject PyGreenlet_Type; /* Defines that customize greenlet module behaviour */ #ifndef GREENLET_USE_GC #define GREENLET_USE_GC 1 #endif #ifndef GREENLET_USE_TRACING #define GREENLET_USE_TRACING 1 #endif /* Weak reference to the switching-to greenlet during the slp switch */ static PyGreenlet* volatile ts_target = NULL; /* Strong reference to the switching from greenlet after the switch */ static PyGreenlet* volatile ts_origin = NULL; /* Strong reference to the current greenlet in this thread state */ static PyGreenlet* volatile ts_current = NULL; /* NULL if error, otherwise args tuple to pass around during slp switch */ static PyObject* volatile ts_passaround_args = NULL; static PyObject* volatile ts_passaround_kwargs = NULL; /***********************************************************/ /* Thread-aware routines, switching global variables when needed */ #define STATE_OK (ts_current->run_info == PyThreadState_GET()->dict \ || !green_updatecurrent()) static PyObject* ts_curkey; static PyObject* ts_delkey; #if GREENLET_USE_TRACING static PyObject* ts_tracekey; static PyObject* ts_event_switch; static PyObject* ts_event_throw; #endif static PyObject* PyExc_GreenletError; static PyObject* PyExc_GreenletExit; static PyObject* ts_empty_tuple; static PyObject* ts_empty_dict; #if GREENLET_USE_GC #define GREENLET_GC_FLAGS Py_TPFLAGS_HAVE_GC #define GREENLET_tp_alloc PyType_GenericAlloc #define GREENLET_tp_free PyObject_GC_Del #define GREENLET_tp_traverse green_traverse #define GREENLET_tp_clear green_clear #define GREENLET_tp_is_gc green_is_gc #else /* GREENLET_USE_GC */ #define GREENLET_GC_FLAGS 0 #define GREENLET_tp_alloc 0 #define GREENLET_tp_free 0 #define GREENLET_tp_traverse 0 #define GREENLET_tp_clear 0 #define GREENLET_tp_is_gc 0 #endif /* !GREENLET_USE_GC */ static PyGreenlet* green_create_main(void) { PyGreenlet* gmain; PyObject* dict = PyThreadState_GetDict(); if (dict == NULL) { if (!PyErr_Occurred()) PyErr_NoMemory(); return NULL; } /* create the main greenlet for this thread */ gmain = (PyGreenlet*) PyType_GenericAlloc(&PyGreenlet_Type, 0); if (gmain == NULL) return NULL; gmain->stack_start = (char*) 1; gmain->stack_stop = (char*) -1; gmain->run_info = dict; Py_INCREF(dict); return gmain; } static int green_updatecurrent(void) { PyObject *exc, *val, *tb; PyThreadState* tstate; PyGreenlet* current; PyGreenlet* previous; PyObject* deleteme; green_updatecurrent_restart: /* save current exception */ PyErr_Fetch(&exc, &val, &tb); /* get ts_current from the active tstate */ tstate = PyThreadState_GET(); if (tstate->dict && (current = (PyGreenlet*) PyDict_GetItem(tstate->dict, ts_curkey))) { /* found -- remove it, to avoid keeping a ref */ Py_INCREF(current); PyDict_DelItem(tstate->dict, ts_curkey); } else { /* first time we see this tstate */ current = green_create_main(); if (current == NULL) { Py_XDECREF(exc); Py_XDECREF(val); Py_XDECREF(tb); return -1; } } assert(current->run_info == tstate->dict); green_updatecurrent_retry: /* update ts_current as soon as possible, in case of nested switches */ Py_INCREF(current); previous = ts_current; ts_current = current; /* save ts_current as the current greenlet of its own thread */ if (PyDict_SetItem(previous->run_info, ts_curkey, (PyObject*) previous)) { Py_DECREF(previous); Py_DECREF(current); Py_XDECREF(exc); Py_XDECREF(val); Py_XDECREF(tb); return -1; } Py_DECREF(previous); /* green_dealloc() cannot delete greenlets from other threads, so it stores them in the thread dict; delete them now. */ deleteme = PyDict_GetItem(tstate->dict, ts_delkey); if (deleteme != NULL) { PyList_SetSlice(deleteme, 0, INT_MAX, NULL); } if (ts_current != current) { /* some Python code executed above and there was a thread switch, * so ts_current points to some other thread again. We need to * delete ts_curkey (it's likely there) and retry. */ PyDict_DelItem(tstate->dict, ts_curkey); goto green_updatecurrent_retry; } /* release an extra reference */ Py_DECREF(current); /* restore current exception */ PyErr_Restore(exc, val, tb); /* thread switch could happen during PyErr_Restore, in that case there's nothing to do except restart from scratch. */ if (ts_current->run_info != tstate->dict) goto green_updatecurrent_restart; return 0; } static PyObject* green_statedict(PyGreenlet* g) { while (!PyGreenlet_STARTED(g)) { g = g->parent; if (g == NULL) { /* garbage collected greenlet in chain */ return NULL; } } return g->run_info; } /***********************************************************/ /* Some functions must not be inlined: * slp_restore_state, when inlined into slp_switch might cause it to restore stack over its own local variables * slp_save_state, when inlined would add its own local variables to the saved stack, wasting space * slp_switch, cannot be inlined for obvious reasons * g_initialstub, when inlined would receive a pointer into its own stack frame, leading to incomplete stack save/restore */ #if defined(__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) #define GREENLET_NOINLINE_SUPPORTED #define GREENLET_NOINLINE(name) __attribute__((noinline)) name #elif defined(_MSC_VER) && (_MSC_VER >= 1300) #define GREENLET_NOINLINE_SUPPORTED #define GREENLET_NOINLINE(name) __declspec(noinline) name #endif #ifdef GREENLET_NOINLINE_SUPPORTED /* add forward declarations */ static void GREENLET_NOINLINE(slp_restore_state)(void); static int GREENLET_NOINLINE(slp_save_state)(char*); #if !(defined(MS_WIN64) && defined(_M_X64)) static int GREENLET_NOINLINE(slp_switch)(void); #endif static int GREENLET_NOINLINE(g_initialstub)(void*); #define GREENLET_NOINLINE_INIT() do {} while(0) #else /* force compiler to call functions via pointers */ static void (*slp_restore_state)(void); static int (*slp_save_state)(char*); static int (*slp_switch)(void); static int (*g_initialstub)(void*); #define GREENLET_NOINLINE(name) cannot_inline_ ## name #define GREENLET_NOINLINE_INIT() do { \ slp_restore_state = GREENLET_NOINLINE(slp_restore_state); \ slp_save_state = GREENLET_NOINLINE(slp_save_state); \ slp_switch = GREENLET_NOINLINE(slp_switch); \ g_initialstub = GREENLET_NOINLINE(g_initialstub); \ } while(0) #endif /* * the following macros are spliced into the OS/compiler * specific code, in order to simplify maintenance. */ #define SLP_SAVE_STATE(stackref, stsizediff) \ stackref += STACK_MAGIC; \ if (slp_save_state((char*)stackref)) return -1; \ if (!PyGreenlet_ACTIVE(ts_target)) return 1; \ stsizediff = ts_target->stack_start - (char*)stackref #define SLP_RESTORE_STATE() \ slp_restore_state() #define SLP_EVAL #define slp_switch GREENLET_NOINLINE(slp_switch) #include "slp_platformselect.h" #undef slp_switch #ifndef STACK_MAGIC #error "greenlet needs to be ported to this platform, or teached how to detect your compiler properly." #endif /* !STACK_MAGIC */ #ifdef EXTERNAL_ASM /* CCP addition: Make these functions, to be called from assembler. * The token include file for the given platform should enable the * EXTERNAL_ASM define so that this is included. */ intptr_t slp_save_state_asm(intptr_t *ref) { intptr_t diff; SLP_SAVE_STATE(ref, diff); return diff; } void slp_restore_state_asm(void) { SLP_RESTORE_STATE(); } extern int slp_switch(void); #endif /***********************************************************/ static int g_save(PyGreenlet* g, char* stop) { /* Save more of g's stack into the heap -- at least up to 'stop' g->stack_stop |________| | | | __ stop . . . . . | | ==> . . |________| _______ | | | | | | | | g->stack_start | | |_______| g->stack_copy */ intptr_t sz1 = g->stack_saved; intptr_t sz2 = stop - g->stack_start; assert(g->stack_start != NULL); if (sz2 > sz1) { char* c = (char*)PyMem_Realloc(g->stack_copy, sz2); if (!c) { PyErr_NoMemory(); return -1; } memcpy(c+sz1, g->stack_start+sz1, sz2-sz1); g->stack_copy = c; g->stack_saved = sz2; } return 0; } static void GREENLET_NOINLINE(slp_restore_state)(void) { PyGreenlet* g = ts_target; PyGreenlet* owner = ts_current; #ifdef SLP_BEFORE_RESTORE_STATE SLP_BEFORE_RESTORE_STATE(); #endif /* Restore the heap copy back into the C stack */ if (g->stack_saved != 0) { memcpy(g->stack_start, g->stack_copy, g->stack_saved); PyMem_Free(g->stack_copy); g->stack_copy = NULL; g->stack_saved = 0; } if (owner->stack_start == NULL) owner = owner->stack_prev; /* greenlet is dying, skip it */ while (owner && owner->stack_stop <= g->stack_stop) owner = owner->stack_prev; /* find greenlet with more stack */ g->stack_prev = owner; } static int GREENLET_NOINLINE(slp_save_state)(char* stackref) { /* must free all the C stack up to target_stop */ char* target_stop = ts_target->stack_stop; PyGreenlet* owner = ts_current; assert(owner->stack_saved == 0); if (owner->stack_start == NULL) owner = owner->stack_prev; /* not saved if dying */ else owner->stack_start = stackref; #ifdef SLP_BEFORE_SAVE_STATE SLP_BEFORE_SAVE_STATE(); #endif while (owner->stack_stop < target_stop) { /* ts_current is entierely within the area to free */ if (g_save(owner, owner->stack_stop)) return -1; /* XXX */ owner = owner->stack_prev; } if (owner != ts_target) { if (g_save(owner, target_stop)) return -1; /* XXX */ } return 0; } static int g_switchstack(void) { /* Perform a stack switch according to some global variables that must be set before: - ts_current: current greenlet (holds a reference) - ts_target: greenlet to switch to (weak reference) - ts_passaround_args: NULL if PyErr_Occurred(), else a tuple of args sent to ts_target (holds a reference) - ts_passaround_kwargs: switch kwargs (holds a reference) On return results are passed via global variables as well: - ts_origin: originating greenlet (holds a reference) - ts_current: current greenlet (holds a reference) - ts_passaround_args: NULL if PyErr_Occurred(), else a tuple of args sent to ts_current (holds a reference) - ts_passaround_kwargs: switch kwargs (holds a reference) It is very important that stack switch is 'atomic', i.e. no calls into other Python code allowed (except very few that are safe), because global variables are very fragile. */ int err; { /* save state */ PyGreenlet* current = ts_current; PyThreadState* tstate = PyThreadState_GET(); current->recursion_depth = tstate->recursion_depth; current->top_frame = tstate->frame; current->exc_type = tstate->exc_type; current->exc_value = tstate->exc_value; current->exc_traceback = tstate->exc_traceback; } err = slp_switch(); if (err < 0) { /* error */ PyGreenlet* current = ts_current; current->top_frame = NULL; current->exc_type = NULL; current->exc_value = NULL; current->exc_traceback = NULL; assert(ts_origin == NULL); ts_target = NULL; } else { PyGreenlet* target = ts_target; PyGreenlet* origin = ts_current; PyThreadState* tstate = PyThreadState_GET(); tstate->recursion_depth = target->recursion_depth; tstate->frame = target->top_frame; target->top_frame = NULL; tstate->exc_type = target->exc_type; target->exc_type = NULL; tstate->exc_value = target->exc_value; target->exc_value = NULL; tstate->exc_traceback = target->exc_traceback; target->exc_traceback = NULL; assert(ts_origin == NULL); Py_INCREF(target); ts_current = target; ts_origin = origin; ts_target = NULL; } return err; } #if GREENLET_USE_TRACING static int g_calltrace(PyObject* tracefunc, PyObject* event, PyGreenlet* origin, PyGreenlet* target) { PyObject *retval; PyObject *exc_type, *exc_val, *exc_tb; PyThreadState *tstate; PyErr_Fetch(&exc_type, &exc_val, &exc_tb); tstate = PyThreadState_GET(); tstate->tracing++; tstate->use_tracing = 0; retval = PyObject_CallFunction(tracefunc, "O(OO)", event, origin, target); tstate->tracing--; tstate->use_tracing = (tstate->tracing <= 0 && ((tstate->c_tracefunc != NULL) || (tstate->c_profilefunc != NULL))); if (retval == NULL) { /* In case of exceptions trace function is removed */ if (PyDict_GetItem(tstate->dict, ts_tracekey)) PyDict_DelItem(tstate->dict, ts_tracekey); Py_XDECREF(exc_type); Py_XDECREF(exc_val); Py_XDECREF(exc_tb); return -1; } else Py_DECREF(retval); PyErr_Restore(exc_type, exc_val, exc_tb); return 0; } #endif static PyObject * g_switch(PyGreenlet* target, PyObject* args, PyObject* kwargs) { /* _consumes_ a reference to the args tuple and kwargs dict, and return a new tuple reference */ int err = 0; PyObject* run_info; /* check ts_current */ if (!STATE_OK) { Py_XDECREF(args); Py_XDECREF(kwargs); return NULL; } run_info = green_statedict(target); if (run_info == NULL || run_info != ts_current->run_info) { Py_XDECREF(args); Py_XDECREF(kwargs); PyErr_SetString(PyExc_GreenletError, run_info ? "cannot switch to a different thread" : "cannot switch to a garbage collected greenlet"); return NULL; } ts_passaround_args = args; ts_passaround_kwargs = kwargs; /* find the real target by ignoring dead greenlets, and if necessary starting a greenlet. */ while (target) { if (PyGreenlet_ACTIVE(target)) { ts_target = target; err = g_switchstack(); break; } if (!PyGreenlet_STARTED(target)) { void* dummymarker; ts_target = target; err = g_initialstub(&dummymarker); if (err == 1) { continue; /* retry the switch */ } break; } target = target->parent; } /* For a very short time, immediately after the 'atomic' g_switchstack() call, global variables are in a known state. We need to save everything we need, before it is destroyed by calls into arbitrary Python code. */ args = ts_passaround_args; ts_passaround_args = NULL; kwargs = ts_passaround_kwargs; ts_passaround_kwargs = NULL; if (err < 0) { /* Turn switch errors into switch throws */ assert(ts_origin == NULL); Py_CLEAR(kwargs); Py_CLEAR(args); } else { PyGreenlet *origin; #if GREENLET_USE_TRACING PyGreenlet *current; PyObject *tracefunc; #endif origin = ts_origin; ts_origin = NULL; #if GREENLET_USE_TRACING current = ts_current; if ((tracefunc = PyDict_GetItem(current->run_info, ts_tracekey)) != NULL) { Py_INCREF(tracefunc); if (g_calltrace(tracefunc, args ? ts_event_switch : ts_event_throw, origin, current) < 0) { /* Turn trace errors into switch throws */ Py_CLEAR(kwargs); Py_CLEAR(args); } Py_DECREF(tracefunc); } #endif Py_DECREF(origin); } /* We need to figure out what values to pass to the target greenlet based on the arguments that have been passed to greenlet.switch(). If switch() was just passed an arg tuple, then we'll just return that. If only keyword arguments were passed, then we'll pass the keyword argument dict. Otherwise, we'll create a tuple of (args, kwargs) and return both. */ if (kwargs == NULL) { return args; } else if (PyDict_Size(kwargs) == 0) { Py_DECREF(kwargs); return args; } else if (PySequence_Length(args) == 0) { Py_DECREF(args); return kwargs; } else { PyObject *tuple = PyTuple_New(2); if (tuple == NULL) { Py_DECREF(args); Py_DECREF(kwargs); return NULL; } PyTuple_SET_ITEM(tuple, 0, args); PyTuple_SET_ITEM(tuple, 1, kwargs); return tuple; } } static PyObject * g_handle_exit(PyObject *result) { if (result == NULL && PyErr_ExceptionMatches(PyExc_GreenletExit)) { /* catch and ignore GreenletExit */ PyObject *exc, *val, *tb; PyErr_Fetch(&exc, &val, &tb); if (val == NULL) { Py_INCREF(Py_None); val = Py_None; } result = val; Py_DECREF(exc); Py_XDECREF(tb); } if (result != NULL) { /* package the result into a 1-tuple */ PyObject *r = result; result = PyTuple_New(1); if (result) { PyTuple_SET_ITEM(result, 0, r); } else { Py_DECREF(r); } } return result; } static int GREENLET_NOINLINE(g_initialstub)(void* mark) { int err; PyObject *o, *run; PyObject *exc, *val, *tb; PyObject *run_info; PyGreenlet* self = ts_target; PyObject* args = ts_passaround_args; PyObject* kwargs = ts_passaround_kwargs; /* save exception in case getattr clears it */ PyErr_Fetch(&exc, &val, &tb); /* self.run is the object to call in the new greenlet */ run = PyObject_GetAttrString((PyObject*) self, "run"); if (run == NULL) { Py_XDECREF(exc); Py_XDECREF(val); Py_XDECREF(tb); return -1; } /* restore saved exception */ PyErr_Restore(exc, val, tb); /* recheck the state in case getattr caused thread switches */ if (!STATE_OK) { Py_DECREF(run); return -1; } /* recheck run_info in case greenlet reparented anywhere above */ run_info = green_statedict(self); if (run_info == NULL || run_info != ts_current->run_info) { Py_DECREF(run); PyErr_SetString(PyExc_GreenletError, run_info ? "cannot switch to a different thread" : "cannot switch to a garbage collected greenlet"); return -1; } /* by the time we got here another start could happen elsewhere, * that means it should now be a regular switch */ if (PyGreenlet_STARTED(self)) { Py_DECREF(run); ts_passaround_args = args; ts_passaround_kwargs = kwargs; return 1; } /* start the greenlet */ self->stack_start = NULL; self->stack_stop = (char*) mark; if (ts_current->stack_start == NULL) { /* ts_current is dying */ self->stack_prev = ts_current->stack_prev; } else { self->stack_prev = ts_current; } self->top_frame = NULL; self->exc_type = NULL; self->exc_value = NULL; self->exc_traceback = NULL; self->recursion_depth = PyThreadState_GET()->recursion_depth; /* restore arguments in case they are clobbered */ ts_target = self; ts_passaround_args = args; ts_passaround_kwargs = kwargs; /* perform the initial switch */ err = g_switchstack(); /* returns twice! The 1st time with err=1: we are in the new greenlet The 2nd time with err=0: back in the caller's greenlet */ if (err == 1) { /* in the new greenlet */ PyGreenlet* origin; #if GREENLET_USE_TRACING PyObject* tracefunc; #endif PyObject* result; PyGreenlet* parent; self->stack_start = (char*) 1; /* running */ /* grab origin while we still can */ origin = ts_origin; ts_origin = NULL; /* now use run_info to store the statedict */ o = self->run_info; self->run_info = green_statedict(self->parent); Py_INCREF(self->run_info); Py_XDECREF(o); #if GREENLET_USE_TRACING if ((tracefunc = PyDict_GetItem(self->run_info, ts_tracekey)) != NULL) { Py_INCREF(tracefunc); if (g_calltrace(tracefunc, args ? ts_event_switch : ts_event_throw, origin, self) < 0) { /* Turn trace errors into switch throws */ Py_CLEAR(kwargs); Py_CLEAR(args); } Py_DECREF(tracefunc); } #endif Py_DECREF(origin); if (args == NULL) { /* pending exception */ result = NULL; } else { /* call g.run(*args, **kwargs) */ result = PyEval_CallObjectWithKeywords( run, args, kwargs); Py_DECREF(args); Py_XDECREF(kwargs); } Py_DECREF(run); result = g_handle_exit(result); /* jump back to parent */ self->stack_start = NULL; /* dead */ for (parent = self->parent; parent != NULL; parent = parent->parent) { result = g_switch(parent, result, NULL); /* Return here means switch to parent failed, * in which case we throw *current* exception * to the next parent in chain. */ assert(result == NULL); } /* We ran out of parents, cannot continue */ PyErr_WriteUnraisable((PyObject *) self); Py_FatalError("greenlets cannot continue"); } /* back in the parent */ if (err < 0) { /* start failed badly, restore greenlet state */ self->stack_start = NULL; self->stack_stop = NULL; self->stack_prev = NULL; } return err; } /***********************************************************/ static PyObject* green_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { PyObject* o = PyBaseObject_Type.tp_new(type, ts_empty_tuple, ts_empty_dict); if (o != NULL) { if (!STATE_OK) { Py_DECREF(o); return NULL; } Py_INCREF(ts_current); ((PyGreenlet*) o)->parent = ts_current; } return o; } static int green_setrun(PyGreenlet* self, PyObject* nrun, void* c); static int green_setparent(PyGreenlet* self, PyObject* nparent, void* c); static int green_init(PyGreenlet *self, PyObject *args, PyObject *kwargs) { PyObject *run = NULL; PyObject* nparent = NULL; static char *kwlist[] = {"run", "parent", 0}; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OO:green", kwlist, &run, &nparent)) return -1; if (run != NULL) { if (green_setrun(self, run, NULL)) return -1; } if (nparent != NULL && nparent != Py_None) return green_setparent(self, nparent, NULL); return 0; } static int kill_greenlet(PyGreenlet* self) { /* Cannot raise an exception to kill the greenlet if it is not running in the same thread! */ if (self->run_info == PyThreadState_GET()->dict) { /* The dying greenlet cannot be a parent of ts_current because the 'parent' field chain would hold a reference */ PyObject* result; PyGreenlet* oldparent; PyGreenlet* tmp; if (!STATE_OK) { return -1; } oldparent = self->parent; self->parent = ts_current; Py_INCREF(self->parent); /* Send the greenlet a GreenletExit exception. */ PyErr_SetNone(PyExc_GreenletExit); result = g_switch(self, NULL, NULL); tmp = self->parent; self->parent = oldparent; Py_XDECREF(tmp); if (result == NULL) return -1; Py_DECREF(result); return 0; } else { /* Not the same thread! Temporarily save the greenlet into its thread's ts_delkey list. */ PyObject* lst; lst = PyDict_GetItem(self->run_info, ts_delkey); if (lst == NULL) { lst = PyList_New(0); if (lst == NULL || PyDict_SetItem(self->run_info, ts_delkey, lst) < 0) return -1; } if (PyList_Append(lst, (PyObject*) self) < 0) return -1; if (!STATE_OK) /* to force ts_delkey to be reconsidered */ return -1; return 0; } } #if GREENLET_USE_GC static int green_traverse(PyGreenlet *self, visitproc visit, void *arg) { /* We must only visit referenced objects, i.e. only objects Py_INCREF'ed by this greenlet (directly or indirectly): - stack_prev is not visited: holds previous stack pointer, but it's not referenced - frames are not visited: alive greenlets are not garbage collected anyway */ Py_VISIT((PyObject*)self->parent); Py_VISIT(self->run_info); Py_VISIT(self->exc_type); Py_VISIT(self->exc_value); Py_VISIT(self->exc_traceback); Py_VISIT(self->dict); return 0; } static int green_is_gc(PyGreenlet* self) { /* Main greenlet can be garbage collected since it can only become unreachable if the underlying thread exited. Active greenlet cannot be garbage collected, however. */ if (PyGreenlet_MAIN(self) || !PyGreenlet_ACTIVE(self)) return 1; return 0; } static int green_clear(PyGreenlet* self) { /* Greenlet is only cleared if it is about to be collected. Since active greenlets are not garbage collectable, we can be sure that, even if they are deallocated during clear, nothing they reference is in unreachable or finalizers, so even if it switches we are relatively safe. */ Py_CLEAR(self->parent); Py_CLEAR(self->run_info); Py_CLEAR(self->exc_type); Py_CLEAR(self->exc_value); Py_CLEAR(self->exc_traceback); Py_CLEAR(self->dict); return 0; } #endif static void green_dealloc(PyGreenlet* self) { PyObject *error_type, *error_value, *error_traceback; #if GREENLET_USE_GC PyObject_GC_UnTrack(self); #endif if (PyGreenlet_ACTIVE(self) && self->run_info != NULL && !PyGreenlet_MAIN(self)) { /* Hacks hacks hacks copied from instance_dealloc() */ /* Temporarily resurrect the greenlet. */ assert(Py_REFCNT(self) == 0); Py_REFCNT(self) = 1; /* Save the current exception, if any. */ PyErr_Fetch(&error_type, &error_value, &error_traceback); if (kill_greenlet(self) < 0) { PyErr_WriteUnraisable((PyObject*) self); /* XXX what else should we do? */ } /* Check for no resurrection must be done while we keep * our internal reference, otherwise PyFile_WriteObject * causes recursion if using Py_INCREF/Py_DECREF */ if (Py_REFCNT(self) == 1 && PyGreenlet_ACTIVE(self)) { /* Not resurrected, but still not dead! XXX what else should we do? we complain. */ PyObject* f = PySys_GetObject("stderr"); Py_INCREF(self); /* leak! */ if (f != NULL) { PyFile_WriteString("GreenletExit did not kill ", f); PyFile_WriteObject((PyObject*) self, f, 0); PyFile_WriteString("\n", f); } } /* Restore the saved exception. */ PyErr_Restore(error_type, error_value, error_traceback); /* Undo the temporary resurrection; can't use DECREF here, * it would cause a recursive call. */ assert(Py_REFCNT(self) > 0); if (--Py_REFCNT(self) != 0) { /* Resurrected! */ Py_ssize_t refcnt = Py_REFCNT(self); _Py_NewReference((PyObject*) self); Py_REFCNT(self) = refcnt; #if GREENLET_USE_GC PyObject_GC_Track((PyObject *)self); #endif _Py_DEC_REFTOTAL; #ifdef COUNT_ALLOCS --Py_TYPE(self)->tp_frees; --Py_TYPE(self)->tp_allocs; #endif /* COUNT_ALLOCS */ return; } } if (self->weakreflist != NULL) PyObject_ClearWeakRefs((PyObject *) self); Py_CLEAR(self->parent); Py_CLEAR(self->run_info); Py_CLEAR(self->exc_type); Py_CLEAR(self->exc_value); Py_CLEAR(self->exc_traceback); Py_CLEAR(self->dict); Py_TYPE(self)->tp_free((PyObject*) self); } static PyObject* single_result(PyObject* results) { if (results != NULL && PyTuple_Check(results) && PyTuple_GET_SIZE(results) == 1) { PyObject *result = PyTuple_GET_ITEM(results, 0); Py_INCREF(result); Py_DECREF(results); return result; } else return results; } static PyObject * throw_greenlet(PyGreenlet *self, PyObject *typ, PyObject *val, PyObject *tb) { /* Note: _consumes_ a reference to typ, val, tb */ PyObject *result = NULL; PyErr_Restore(typ, val, tb); if (PyGreenlet_STARTED(self) && !PyGreenlet_ACTIVE(self)) { /* dead greenlet: turn GreenletExit into a regular return */ result = g_handle_exit(result); } return single_result(g_switch(self, result, NULL)); } PyDoc_STRVAR(green_switch_doc, "switch(*args, **kwargs)\n" "\n" "Switch execution to this greenlet.\n" "\n" "If this greenlet has never been run, then this greenlet\n" "will be switched to using the body of self.run(*args, **kwargs).\n" "\n" "If the greenlet is active (has been run, but was switch()'ed\n" "out before leaving its run function), then this greenlet will\n" "be resumed and the return value to its switch call will be\n" "None if no arguments are given, the given argument if one\n" "argument is given, or the args tuple and keyword args dict if\n" "multiple arguments are given.\n" "\n" "If the greenlet is dead, or is the current greenlet then this\n" "function will simply return the arguments using the same rules as\n" "above.\n"); static PyObject* green_switch( PyGreenlet* self, PyObject* args, PyObject* kwargs) { Py_INCREF(args); Py_XINCREF(kwargs); return single_result(g_switch(self, args, kwargs)); } /* Macros required to support Python < 2.6 for green_throw() */ #ifndef PyExceptionClass_Check # define PyExceptionClass_Check PyClass_Check #endif #ifndef PyExceptionInstance_Check # define PyExceptionInstance_Check PyInstance_Check #endif #ifndef PyExceptionInstance_Class # define PyExceptionInstance_Class(x) \ ((PyObject *) ((PyInstanceObject *)(x))->in_class) #endif PyDoc_STRVAR(green_throw_doc, "Switches execution to the greenlet ``g``, but immediately raises the\n" "given exception in ``g``. If no argument is provided, the exception\n" "defaults to ``greenlet.GreenletExit``. The normal exception\n" "propagation rules apply, as described above. Note that calling this\n" "method is almost equivalent to the following::\n" "\n" " def raiser():\n" " raise typ, val, tb\n" " g_raiser = greenlet(raiser, parent=g)\n" " g_raiser.switch()\n" "\n" "except that this trick does not work for the\n" "``greenlet.GreenletExit`` exception, which would not propagate\n" "from ``g_raiser`` to ``g``.\n"); static PyObject * green_throw(PyGreenlet *self, PyObject *args) { PyObject *typ = PyExc_GreenletExit; PyObject *val = NULL; PyObject *tb = NULL; if (!PyArg_ParseTuple(args, "|OOO:throw", &typ, &val, &tb)) { return NULL; } /* First, check the traceback argument, replacing None, with NULL */ if (tb == Py_None) { tb = NULL; } else if (tb != NULL && !PyTraceBack_Check(tb)) { PyErr_SetString( PyExc_TypeError, "throw() third argument must be a traceback object"); return NULL; } Py_INCREF(typ); Py_XINCREF(val); Py_XINCREF(tb); if (PyExceptionClass_Check(typ)) { PyErr_NormalizeException(&typ, &val, &tb); } else if (PyExceptionInstance_Check(typ)) { /* Raising an instance. The value should be a dummy. */ if (val && val != Py_None) { PyErr_SetString( PyExc_TypeError, "instance exception may not have a separate value"); goto failed_throw; } else { /* Normalize to raise , */ Py_XDECREF(val); val = typ; typ = PyExceptionInstance_Class(typ); Py_INCREF(typ); } } else { /* Not something you can raise. throw() fails. */ PyErr_Format( PyExc_TypeError, "exceptions must be classes, or instances, not %s", Py_TYPE(typ)->tp_name); goto failed_throw; } return throw_greenlet(self, typ, val, tb); failed_throw: /* Didn't use our arguments, so restore their original refcounts */ Py_DECREF(typ); Py_XDECREF(val); Py_XDECREF(tb); return NULL; } static int green_bool(PyGreenlet* self) { return PyGreenlet_ACTIVE(self); } static PyObject* green_getdict(PyGreenlet* self, void* c) { if (self->dict == NULL) { self->dict = PyDict_New(); if (self->dict == NULL) return NULL; } Py_INCREF(self->dict); return self->dict; } static int green_setdict(PyGreenlet* self, PyObject* val, void* c) { PyObject* tmp; if (val == NULL) { PyErr_SetString(PyExc_TypeError, "__dict__ may not be deleted"); return -1; } if (!PyDict_Check(val)) { PyErr_SetString(PyExc_TypeError, "__dict__ must be a dictionary"); return -1; } tmp = self->dict; Py_INCREF(val); self->dict = val; Py_XDECREF(tmp); return 0; } static PyObject* green_getdead(PyGreenlet* self, void* c) { if (PyGreenlet_ACTIVE(self) || !PyGreenlet_STARTED(self)) Py_RETURN_FALSE; else Py_RETURN_TRUE; } static PyObject* green_get_stack_saved(PyGreenlet* self, void* c) { return PyLong_FromSsize_t(self->stack_saved); } static PyObject* green_getrun(PyGreenlet* self, void* c) { if (PyGreenlet_STARTED(self) || self->run_info == NULL) { PyErr_SetString(PyExc_AttributeError, "run"); return NULL; } Py_INCREF(self->run_info); return self->run_info; } static int green_setrun(PyGreenlet* self, PyObject* nrun, void* c) { PyObject* o; if (PyGreenlet_STARTED(self)) { PyErr_SetString(PyExc_AttributeError, "run cannot be set " "after the start of the greenlet"); return -1; } o = self->run_info; self->run_info = nrun; Py_XINCREF(nrun); Py_XDECREF(o); return 0; } static PyObject* green_getparent(PyGreenlet* self, void* c) { PyObject* result = self->parent ? (PyObject*) self->parent : Py_None; Py_INCREF(result); return result; } static int green_setparent(PyGreenlet* self, PyObject* nparent, void* c) { PyGreenlet* p; PyObject* run_info = NULL; if (nparent == NULL) { PyErr_SetString(PyExc_AttributeError, "can't delete attribute"); return -1; } if (!PyGreenlet_Check(nparent)) { PyErr_SetString(PyExc_TypeError, "parent must be a greenlet"); return -1; } for (p=(PyGreenlet*) nparent; p; p=p->parent) { if (p == self) { PyErr_SetString(PyExc_ValueError, "cyclic parent chain"); return -1; } run_info = PyGreenlet_ACTIVE(p) ? p->run_info : NULL; } if (run_info == NULL) { PyErr_SetString(PyExc_ValueError, "parent must not be garbage collected"); return -1; } if (PyGreenlet_STARTED(self) && self->run_info != run_info) { PyErr_SetString(PyExc_ValueError, "parent cannot be on a different thread"); return -1; } p = self->parent; self->parent = (PyGreenlet*) nparent; Py_INCREF(nparent); Py_XDECREF(p); return 0; } static PyObject* green_getframe(PyGreenlet* self, void* c) { PyObject* result = self->top_frame ? (PyObject*) self->top_frame : Py_None; Py_INCREF(result); return result; } static PyObject* green_getstate(PyGreenlet* self) { PyErr_Format(PyExc_TypeError, "cannot serialize '%s' object", Py_TYPE(self)->tp_name); return NULL; } /***************************************************************************** * C interface * * These are exported using the CObject API */ static PyGreenlet * PyGreenlet_GetCurrent(void) { if (!STATE_OK) { return NULL; } Py_INCREF(ts_current); return ts_current; } static int PyGreenlet_SetParent(PyGreenlet *g, PyGreenlet *nparent) { if (!PyGreenlet_Check(g)) { PyErr_SetString(PyExc_TypeError, "parent must be a greenlet"); return -1; } return green_setparent((PyGreenlet*) g, (PyObject *) nparent, NULL); } static PyGreenlet * PyGreenlet_New(PyObject *run, PyGreenlet *parent) { PyGreenlet* g = NULL; g = (PyGreenlet *) PyType_GenericAlloc(&PyGreenlet_Type, 0); if (g == NULL) { return NULL; } if (run != NULL) { Py_INCREF(run); g->run_info = run; } if (parent != NULL) { if (PyGreenlet_SetParent(g, parent)) { Py_DECREF(g); return NULL; } } else { if ((g->parent = PyGreenlet_GetCurrent()) == NULL) { Py_DECREF(g); return NULL; } } return g; } static PyObject * PyGreenlet_Switch(PyGreenlet *g, PyObject *args, PyObject *kwargs) { PyGreenlet *self = (PyGreenlet *) g; if (!PyGreenlet_Check(self)) { PyErr_BadArgument(); return NULL; } if (args == NULL) { args = Py_BuildValue("()"); } else { Py_INCREF(args); } if (kwargs != NULL && PyDict_Check(kwargs)) { Py_INCREF(kwargs); } else { kwargs = NULL; } return single_result(g_switch(self, args, kwargs)); } static PyObject * PyGreenlet_Throw(PyGreenlet *self, PyObject *typ, PyObject *val, PyObject *tb) { if (!PyGreenlet_Check(self)) { PyErr_BadArgument(); return NULL; } Py_INCREF(typ); Py_XINCREF(val); Py_XINCREF(tb); return throw_greenlet(self, typ, val, tb); } /** End C API ****************************************************************/ static PyMethodDef green_methods[] = { {"switch", (PyCFunction)green_switch, METH_VARARGS | METH_KEYWORDS, green_switch_doc}, {"throw", (PyCFunction)green_throw, METH_VARARGS, green_throw_doc}, {"__getstate__", (PyCFunction)green_getstate, METH_NOARGS, NULL}, {NULL, NULL} /* sentinel */ }; static PyGetSetDef green_getsets[] = { {"__dict__", (getter)green_getdict, (setter)green_setdict, /*XXX*/ NULL}, {"run", (getter)green_getrun, (setter)green_setrun, /*XXX*/ NULL}, {"parent", (getter)green_getparent, (setter)green_setparent, /*XXX*/ NULL}, {"gr_frame", (getter)green_getframe, NULL, /*XXX*/ NULL}, {"dead", (getter)green_getdead, NULL, /*XXX*/ NULL}, {"_stack_saved", (getter)green_get_stack_saved, NULL, /*XXX*/ NULL}, {NULL} }; static PyNumberMethods green_as_number = { NULL, /* nb_add */ NULL, /* nb_subtract */ NULL, /* nb_multiply */ #if PY_MAJOR_VERSION < 3 NULL, /* nb_divide */ #endif NULL, /* nb_remainder */ NULL, /* nb_divmod */ NULL, /* nb_power */ NULL, /* nb_negative */ NULL, /* nb_positive */ NULL, /* nb_absolute */ (inquiry)green_bool, /* nb_bool */ }; PyTypeObject PyGreenlet_Type = { PyVarObject_HEAD_INIT(NULL, 0) "greenlet.greenlet", /* tp_name */ sizeof(PyGreenlet), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)green_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ &green_as_number, /* tp_as _number*/ 0, /* tp_as _sequence*/ 0, /* tp_as _mapping*/ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | GREENLET_GC_FLAGS, /* tp_flags */ "greenlet(run=None, parent=None) -> greenlet\n\n" "Creates a new greenlet object (without running it).\n\n" " - *run* -- The callable to invoke.\n" " - *parent* -- The parent greenlet. The default is the current " "greenlet.", /* tp_doc */ (traverseproc)GREENLET_tp_traverse, /* tp_traverse */ (inquiry)GREENLET_tp_clear, /* tp_clear */ 0, /* tp_richcompare */ offsetof(PyGreenlet, weakreflist), /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ green_methods, /* tp_methods */ 0, /* tp_members */ green_getsets, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ offsetof(PyGreenlet, dict), /* tp_dictoffset */ (initproc)green_init, /* tp_init */ GREENLET_tp_alloc, /* tp_alloc */ green_new, /* tp_new */ GREENLET_tp_free, /* tp_free */ (inquiry)GREENLET_tp_is_gc, /* tp_is_gc */ }; static PyObject* mod_getcurrent(PyObject* self) { if (!STATE_OK) return NULL; Py_INCREF(ts_current); return (PyObject*) ts_current; } #if GREENLET_USE_TRACING static PyObject* mod_settrace(PyObject* self, PyObject* args) { int err; PyObject* previous; PyObject* tracefunc; PyGreenlet* current; if (!PyArg_ParseTuple(args, "O", &tracefunc)) return NULL; if (!STATE_OK) return NULL; current = ts_current; previous = PyDict_GetItem(current->run_info, ts_tracekey); if (previous == NULL) previous = Py_None; Py_INCREF(previous); if (tracefunc == Py_None) err = previous != Py_None ? PyDict_DelItem(current->run_info, ts_tracekey) : 0; else err = PyDict_SetItem(current->run_info, ts_tracekey, tracefunc); if (err < 0) Py_CLEAR(previous); return previous; } static PyObject* mod_gettrace(PyObject* self) { PyObject* tracefunc; if (!STATE_OK) return NULL; tracefunc = PyDict_GetItem(ts_current->run_info, ts_tracekey); if (tracefunc == NULL) tracefunc = Py_None; Py_INCREF(tracefunc); return tracefunc; } #endif static PyMethodDef GreenMethods[] = { {"getcurrent", (PyCFunction)mod_getcurrent, METH_NOARGS, /*XXX*/ NULL}, #if GREENLET_USE_TRACING {"settrace", (PyCFunction)mod_settrace, METH_VARARGS, NULL}, {"gettrace", (PyCFunction)mod_gettrace, METH_NOARGS, NULL}, #endif {NULL, NULL} /* Sentinel */ }; static char* copy_on_greentype[] = { "getcurrent", "error", "GreenletExit", #if GREENLET_USE_TRACING "settrace", "gettrace", #endif NULL }; #if PY_MAJOR_VERSION >= 3 #define INITERROR return NULL static struct PyModuleDef greenlet_module_def = { PyModuleDef_HEAD_INIT, "greenlet", NULL, -1, GreenMethods, }; PyMODINIT_FUNC PyInit_greenlet(void) #else #define INITERROR return PyMODINIT_FUNC initgreenlet(void) #endif { PyObject* m = NULL; char** p = NULL; PyObject *c_api_object; static void *_PyGreenlet_API[PyGreenlet_API_pointers]; GREENLET_NOINLINE_INIT(); #if PY_MAJOR_VERSION >= 3 m = PyModule_Create(&greenlet_module_def); #else m = Py_InitModule("greenlet", GreenMethods); #endif if (m == NULL) { INITERROR; } if (PyModule_AddStringConstant(m, "__version__", GREENLET_VERSION) < 0) { INITERROR; } #if PY_MAJOR_VERSION >= 3 ts_curkey = PyUnicode_InternFromString("__greenlet_ts_curkey"); ts_delkey = PyUnicode_InternFromString("__greenlet_ts_delkey"); #if GREENLET_USE_TRACING ts_tracekey = PyUnicode_InternFromString("__greenlet_ts_tracekey"); ts_event_switch = PyUnicode_InternFromString("switch"); ts_event_throw = PyUnicode_InternFromString("throw"); #endif #else ts_curkey = PyString_InternFromString("__greenlet_ts_curkey"); ts_delkey = PyString_InternFromString("__greenlet_ts_delkey"); #if GREENLET_USE_TRACING ts_tracekey = PyString_InternFromString("__greenlet_ts_tracekey"); ts_event_switch = PyString_InternFromString("switch"); ts_event_throw = PyString_InternFromString("throw"); #endif #endif if (ts_curkey == NULL || ts_delkey == NULL) { INITERROR; } if (PyType_Ready(&PyGreenlet_Type) < 0) { INITERROR; } PyExc_GreenletError = PyErr_NewException("greenlet.error", NULL, NULL); if (PyExc_GreenletError == NULL) { INITERROR; } #if PY_MAJOR_VERSION >= 3 || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION >= 5) PyExc_GreenletExit = PyErr_NewException("greenlet.GreenletExit", PyExc_BaseException, NULL); #else PyExc_GreenletExit = PyErr_NewException("greenlet.GreenletExit", NULL, NULL); #endif if (PyExc_GreenletExit == NULL) { INITERROR; } ts_empty_tuple = PyTuple_New(0); if (ts_empty_tuple == NULL) { INITERROR; } ts_empty_dict = PyDict_New(); if (ts_empty_dict == NULL) { INITERROR; } ts_current = green_create_main(); if (ts_current == NULL) { INITERROR; } Py_INCREF(&PyGreenlet_Type); PyModule_AddObject(m, "greenlet", (PyObject*) &PyGreenlet_Type); Py_INCREF(PyExc_GreenletError); PyModule_AddObject(m, "error", PyExc_GreenletError); Py_INCREF(PyExc_GreenletExit); PyModule_AddObject(m, "GreenletExit", PyExc_GreenletExit); PyModule_AddObject(m, "GREENLET_USE_GC", PyBool_FromLong(GREENLET_USE_GC)); PyModule_AddObject(m, "GREENLET_USE_TRACING", PyBool_FromLong(GREENLET_USE_TRACING)); /* also publish module-level data as attributes of the greentype. */ for (p=copy_on_greentype; *p; p++) { PyObject* o = PyObject_GetAttrString(m, *p); if (!o) continue; PyDict_SetItemString(PyGreenlet_Type.tp_dict, *p, o); Py_DECREF(o); } /* * Expose C API */ /* types */ _PyGreenlet_API[PyGreenlet_Type_NUM] = (void *) &PyGreenlet_Type; /* exceptions */ _PyGreenlet_API[PyExc_GreenletError_NUM] = (void *) PyExc_GreenletError; _PyGreenlet_API[PyExc_GreenletExit_NUM] = (void *) PyExc_GreenletExit; /* methods */ _PyGreenlet_API[PyGreenlet_New_NUM] = (void *) PyGreenlet_New; _PyGreenlet_API[PyGreenlet_GetCurrent_NUM] = (void *) PyGreenlet_GetCurrent; _PyGreenlet_API[PyGreenlet_Throw_NUM] = (void *) PyGreenlet_Throw; _PyGreenlet_API[PyGreenlet_Switch_NUM] = (void *) PyGreenlet_Switch; _PyGreenlet_API[PyGreenlet_SetParent_NUM] = (void *) PyGreenlet_SetParent; #ifdef GREENLET_USE_PYCAPSULE c_api_object = PyCapsule_New((void *) _PyGreenlet_API, "greenlet._C_API", NULL); #else c_api_object = PyCObject_FromVoidPtr((void *) _PyGreenlet_API, NULL); #endif if (c_api_object != NULL) { PyModule_AddObject(m, "_C_API", c_api_object); } #if PY_MAJOR_VERSION >= 3 return m; #endif } greenlet-0.4.12/greenlet.h000066400000000000000000000073271304443540200153710ustar00rootroot00000000000000/* vim:set noet ts=8 sw=8 : */ /* Greenlet object interface */ #ifndef Py_GREENLETOBJECT_H #define Py_GREENLETOBJECT_H #include #ifdef __cplusplus extern "C" { #endif #define GREENLET_VERSION "0.4.12" typedef struct _greenlet { PyObject_HEAD char* stack_start; char* stack_stop; char* stack_copy; intptr_t stack_saved; struct _greenlet* stack_prev; struct _greenlet* parent; PyObject* run_info; struct _frame* top_frame; int recursion_depth; PyObject* weakreflist; PyObject* exc_type; PyObject* exc_value; PyObject* exc_traceback; PyObject* dict; } PyGreenlet; #define PyGreenlet_Check(op) PyObject_TypeCheck(op, &PyGreenlet_Type) #define PyGreenlet_MAIN(op) (((PyGreenlet*)(op))->stack_stop == (char*) -1) #define PyGreenlet_STARTED(op) (((PyGreenlet*)(op))->stack_stop != NULL) #define PyGreenlet_ACTIVE(op) (((PyGreenlet*)(op))->stack_start != NULL) #define PyGreenlet_GET_PARENT(op) (((PyGreenlet*)(op))->parent) #if (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION >= 7) || (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 1) || PY_MAJOR_VERSION > 3 #define GREENLET_USE_PYCAPSULE #endif /* C API functions */ /* Total number of symbols that are exported */ #define PyGreenlet_API_pointers 8 #define PyGreenlet_Type_NUM 0 #define PyExc_GreenletError_NUM 1 #define PyExc_GreenletExit_NUM 2 #define PyGreenlet_New_NUM 3 #define PyGreenlet_GetCurrent_NUM 4 #define PyGreenlet_Throw_NUM 5 #define PyGreenlet_Switch_NUM 6 #define PyGreenlet_SetParent_NUM 7 #ifndef GREENLET_MODULE /* This section is used by modules that uses the greenlet C API */ static void **_PyGreenlet_API = NULL; #define PyGreenlet_Type (*(PyTypeObject *) _PyGreenlet_API[PyGreenlet_Type_NUM]) #define PyExc_GreenletError \ ((PyObject *) _PyGreenlet_API[PyExc_GreenletError_NUM]) #define PyExc_GreenletExit \ ((PyObject *) _PyGreenlet_API[PyExc_GreenletExit_NUM]) /* * PyGreenlet_New(PyObject *args) * * greenlet.greenlet(run, parent=None) */ #define PyGreenlet_New \ (* (PyGreenlet * (*)(PyObject *run, PyGreenlet *parent)) \ _PyGreenlet_API[PyGreenlet_New_NUM]) /* * PyGreenlet_GetCurrent(void) * * greenlet.getcurrent() */ #define PyGreenlet_GetCurrent \ (* (PyGreenlet * (*)(void)) _PyGreenlet_API[PyGreenlet_GetCurrent_NUM]) /* * PyGreenlet_Throw( * PyGreenlet *greenlet, * PyObject *typ, * PyObject *val, * PyObject *tb) * * g.throw(...) */ #define PyGreenlet_Throw \ (* (PyObject * (*) \ (PyGreenlet *self, PyObject *typ, PyObject *val, PyObject *tb)) \ _PyGreenlet_API[PyGreenlet_Throw_NUM]) /* * PyGreenlet_Switch(PyGreenlet *greenlet, PyObject *args) * * g.switch(*args, **kwargs) */ #define PyGreenlet_Switch \ (* (PyObject * (*)(PyGreenlet *greenlet, PyObject *args, PyObject *kwargs)) \ _PyGreenlet_API[PyGreenlet_Switch_NUM]) /* * PyGreenlet_SetParent(PyObject *greenlet, PyObject *new_parent) * * g.parent = new_parent */ #define PyGreenlet_SetParent \ (* (int (*)(PyGreenlet *greenlet, PyGreenlet *nparent)) \ _PyGreenlet_API[PyGreenlet_SetParent_NUM]) /* Macro that imports greenlet and initializes C API */ #ifdef GREENLET_USE_PYCAPSULE #define PyGreenlet_Import() \ { \ _PyGreenlet_API = (void**)PyCapsule_Import("greenlet._C_API", 0); \ } #else #define PyGreenlet_Import() \ { \ PyObject *module = PyImport_ImportModule("greenlet"); \ if (module != NULL) { \ PyObject *c_api_object = PyObject_GetAttrString( \ module, "_C_API"); \ if (c_api_object != NULL && PyCObject_Check(c_api_object)) { \ _PyGreenlet_API = \ (void **) PyCObject_AsVoidPtr(c_api_object); \ Py_DECREF(c_api_object); \ } \ Py_DECREF(module); \ } \ } #endif #endif /* GREENLET_MODULE */ #ifdef __cplusplus } #endif #endif /* !Py_GREENLETOBJECT_H */ greenlet-0.4.12/make-manifest000077500000000000000000000010151304443540200160460ustar00rootroot00000000000000#! /usr/bin/env python # this file can be used as pre-commit hook with the following command: # # ln -sf ../../make-manifest .git/hooks/pre-commit # import os def main(): files = sorted(set([x.strip() for x in os.popen("git ls-files")]) - set(("make-manifest", ".gitignore"))) with open("MANIFEST.in", "w") as f: for x in files: f.write("include %s\n" % x) os.system("git add MANIFEST.in; git diff --cached MANIFEST.in | cat") if __name__ == '__main__': main() greenlet-0.4.12/make-manylinux000077500000000000000000000010071304443540200162650ustar00rootroot00000000000000#!/bin/bash set -e export PYTHONUNBUFFERED=1 export PYTHONDONTWRITEBYTECODE=1 if [ -d /greenlet -a -d /opt/python ]; then # Running inside docker cd /greenlet rm -rf wheelhouse for variant in /opt/python/*; do rm -rf dist build *.egg-info $variant/bin/python setup.py clean --all bdist_wheel auditwheel repair dist/*.whl done rm -rf dist build *.egg-info exit 0 fi docker run --rm -ti -v "$(pwd):/greenlet:Z" quay.io/pypa/manylinux1_x86_64 /greenlet/$(basename $0) greenlet-0.4.12/make-win-release000077500000000000000000000023611304443540200164600ustar00rootroot00000000000000#! /usr/bin/env python import sys, os, subprocess, re common_dist = ("bdist_wheel", "bdist_egg", "test") pyver2dist = { "Python26": common_dist, "Python26x64": common_dist, "Python27": common_dist, "Python27x64": common_dist, "Python33": common_dist, "Python33x64": common_dist, "Python34": common_dist, "Python34x64": common_dist, "Python35": common_dist, "Python35x64": common_dist, } def system(cmd): sys.stdout.write("====> Running %s\n" % cmd) popen = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True) nl = True while 1: char = popen.stdout.read(1) if not char: break if nl: sys.stdout.write(" ") sys.stdout.write(char) sys.stdout.flush() nl = char == "\n" st = popen.wait() if st != 0: sys.exit("Error: command %r failed" % cmd) sys.stdout.write("\n") def main(): here = os.path.dirname(os.path.dirname(sys.executable)) for pyver, dists in sorted(pyver2dist.items()): exe = os.path.join(here, pyver, "python.exe") for d in dists: cmd = "%s setup.py -q %s" % (exe, d) system(cmd) if __name__ == "__main__": main() greenlet-0.4.12/my_build_ext.py000066400000000000000000000036641304443540200164510ustar00rootroot00000000000000# this code has been taken from gevent's setup.py file. it provides a # build_ext command that puts .so/.pyd files in-place (like "setup.py # build_ext -i"). it uses symlinks if possible and will rebuild when # changing the python version (unlike "setup.py build_ext -i") import sys, os, shutil from distutils.command.build_ext import build_ext as _build_ext def symlink_or_copy(src, dst): if hasattr(os, 'symlink'): try: os.symlink(src, dst) return except OSError: # symbolic link privilege not held?? pass except NotImplementedError: # running on XP/'CreateSymbolicLinkW not found' pass shutil.copyfile(src, dst) class build_ext(_build_ext): """Command for building extensions Prepends library directory to sys.path on normal builds (for tests). Otherwise it forces a non-inplace build and symlinks libraries instead. """ def initialize_options(self): self.my_inplace = None _build_ext.initialize_options(self) def finalize_options(self): if self.my_inplace is None: self.my_inplace = self.inplace self.inplace = 0 _build_ext.finalize_options(self) def build_extension(self, ext): _build_ext.build_extension(self, ext) if not self.my_inplace: build_lib = os.path.abspath(self.build_lib) if build_lib not in sys.path: sys.path.insert(0, build_lib) return filename = self.get_ext_filename(ext.name) build_path = os.path.abspath(os.path.join(self.build_lib, filename)) src_path = os.path.abspath(filename) if build_path != src_path: try: os.unlink(src_path) except OSError: pass if self.verbose: sys.stderr.write('Linking %s to %s\n' % (build_path, src_path)) symlink_or_copy(build_path, src_path) greenlet-0.4.12/platform/000077500000000000000000000000001304443540200152265ustar00rootroot00000000000000greenlet-0.4.12/platform/switch_aarch64_gcc.h000066400000000000000000000045211304443540200210260ustar00rootroot00000000000000/* * this is the internal transfer function. * * HISTORY * 07-Sep-16 Add clang support using x register naming. Fredrik Fornwall * 13-Apr-13 Add support for strange GCC caller-save decisions * 08-Apr-13 File creation. Michael Matz * * NOTES * * Simply save all callee saved registers * */ #define STACK_REFPLUS 1 #ifdef SLP_EVAL #define STACK_MAGIC 0 #define REGS_TO_SAVE "x19", "x20", "x21", "x22", "x23", "x24", "x25", "x26", \ "x27", "x28", "x30" /* aka lr */, \ "v8", "v9", "v10", "v11", \ "v12", "v13", "v14", "v15" static int slp_switch(void) { int err; void *fp; register long *stackref, stsizediff; __asm__ volatile ("" : : : REGS_TO_SAVE); __asm__ volatile ("str x29, %0" : "=m"(fp) : : ); __asm__ ("mov %0, sp" : "=r" (stackref)); { SLP_SAVE_STATE(stackref, stsizediff); __asm__ volatile ( "add sp,sp,%0\n" "add x29,x29,%0\n" : : "r" (stsizediff) ); SLP_RESTORE_STATE(); /* SLP_SAVE_STATE macro contains some return statements (of -1 and 1). It falls through only when the return value of slp_save_state() is zero, which is placed in x0. In that case we (slp_switch) also want to return zero (also in x0 of course). Now, some GCC versions (seen with 4.8) think it's a good idea to save/restore x0 around the call to slp_restore_state(), instead of simply zeroing it at the return below. But slp_restore_state writes random values to the stack slot used for this save/restore (from when it once was saved above in SLP_SAVE_STATE, when it was still uninitialized), so "restoring" that precious zero actually makes us return random values. There are some ways to make GCC not use that zero value in the normal return path (e.g. making err volatile, but that costs a little stack space), and the simplest is to call a function that returns an unknown value (which happens to be zero), so the saved/restored value is unused. */ __asm__ volatile ("mov %0, #0" : "=r" (err)); } __asm__ volatile ("ldr x29, %0" : : "m" (fp) :); __asm__ volatile ("" : : : REGS_TO_SAVE); return err; } #endif greenlet-0.4.12/platform/switch_amd64_unix.h000066400000000000000000000050621304443540200207410ustar00rootroot00000000000000/* * this is the internal transfer function. * * HISTORY * 3-May-13 Ralf Schmitt * Add support for strange GCC caller-save decisions * (ported from switch_aarch64_gcc.h) * 18-Aug-11 Alexey Borzenkov * Correctly save rbp, csr and cw * 01-Apr-04 Hye-Shik Chang * Ported from i386 to amd64. * 24-Nov-02 Christian Tismer * needed to add another magic constant to insure * that f in slp_eval_frame(PyFrameObject *f) * STACK_REFPLUS will probably be 1 in most cases. * gets included into the saved stack area. * 17-Sep-02 Christian Tismer * after virtualizing stack save/restore, the * stack size shrunk a bit. Needed to introduce * an adjustment STACK_MAGIC per platform. * 15-Sep-02 Gerd Woetzel * slightly changed framework for spark * 31-Avr-02 Armin Rigo * Added ebx, esi and edi register-saves. * 01-Mar-02 Samual M. Rushing * Ported from i386. */ #define STACK_REFPLUS 1 #ifdef SLP_EVAL /* #define STACK_MAGIC 3 */ /* the above works fine with gcc 2.96, but 2.95.3 wants this */ #define STACK_MAGIC 0 #define REGS_TO_SAVE "r12", "r13", "r14", "r15" static int slp_switch(void) { int err; void* rbp; void* rbx; unsigned int csr; unsigned short cw; register long *stackref, stsizediff; __asm__ volatile ("" : : : REGS_TO_SAVE); __asm__ volatile ("fstcw %0" : "=m" (cw)); __asm__ volatile ("stmxcsr %0" : "=m" (csr)); __asm__ volatile ("movq %%rbp, %0" : "=m" (rbp)); __asm__ volatile ("movq %%rbx, %0" : "=m" (rbx)); __asm__ ("movq %%rsp, %0" : "=g" (stackref)); { SLP_SAVE_STATE(stackref, stsizediff); __asm__ volatile ( "addq %0, %%rsp\n" "addq %0, %%rbp\n" : : "r" (stsizediff) ); SLP_RESTORE_STATE(); __asm__ volatile ("xorq %%rax, %%rax" : "=a" (err)); } __asm__ volatile ("movq %0, %%rbx" : : "m" (rbx)); __asm__ volatile ("movq %0, %%rbp" : : "m" (rbp)); __asm__ volatile ("ldmxcsr %0" : : "m" (csr)); __asm__ volatile ("fldcw %0" : : "m" (cw)); __asm__ volatile ("" : : : REGS_TO_SAVE); return err; } #endif /* * further self-processing support */ /* * if you want to add self-inspection tools, place them * here. See the x86_msvc for the necessary defines. * These features are highly experimental und not * essential yet. */ greenlet-0.4.12/platform/switch_arm32_gcc.h000066400000000000000000000046701304443540200205270ustar00rootroot00000000000000/* * this is the internal transfer function. * * HISTORY * 14-Aug-06 File creation. Ported from Arm Thumb. Sylvain Baro * 3-Sep-06 Commented out saving of r1-r3 (r4 already commented out) as I * read that these do not need to be saved. Also added notes and * errors related to the frame pointer. Richard Tew. * * NOTES * * It is not possible to detect if fp is used or not, so the supplied * switch function needs to support it, so that you can remove it if * it does not apply to you. * * POSSIBLE ERRORS * * "fp cannot be used in asm here" * * - Try commenting out "fp" in REGS_TO_SAVE. * */ #define STACK_REFPLUS 1 #ifdef SLP_EVAL #define STACK_MAGIC 0 #define REG_SP "sp" #define REG_SPSP "sp,sp" #ifdef __thumb__ #define REG_FP "r7" #define REG_FPFP "r7,r7" #define REGS_TO_SAVE_GENERAL "r4", "r5", "r6", "r8", "r9", "r10", "r11", "lr" #else #define REG_FP "fp" #define REG_FPFP "fp,fp" #define REGS_TO_SAVE_GENERAL "r4", "r5", "r6", "r7", "r8", "r9", "r10", "lr" #endif #if defined(__SOFTFP__) #define REGS_TO_SAVE REGS_TO_SAVE_GENERAL #elif defined(__VFP_FP__) #define REGS_TO_SAVE REGS_TO_SAVE_GENERAL, "d8", "d9", "d10", "d11", \ "d12", "d13", "d14", "d15" #elif defined(__MAVERICK__) #define REGS_TO_SAVE REGS_TO_SAVE_GENERAL, "mvf4", "mvf5", "mvf6", "mvf7", \ "mvf8", "mvf9", "mvf10", "mvf11", \ "mvf12", "mvf13", "mvf14", "mvf15" #else #define REGS_TO_SAVE REGS_TO_SAVE_GENERAL, "f4", "f5", "f6", "f7" #endif static int #ifdef __GNUC__ __attribute__((optimize("no-omit-frame-pointer"))) #endif slp_switch(void) { void *fp; register int *stackref, stsizediff; int result; __asm__ volatile ("" : : : REGS_TO_SAVE); __asm__ volatile ("mov r0," REG_FP "\n\tstr r0,%0" : "=m" (fp) : : "r0"); __asm__ ("mov %0," REG_SP : "=r" (stackref)); { SLP_SAVE_STATE(stackref, stsizediff); __asm__ volatile ( "add " REG_SPSP ",%0\n" "add " REG_FPFP ",%0\n" : : "r" (stsizediff) ); SLP_RESTORE_STATE(); } __asm__ volatile ("ldr r0,%1\n\tmov " REG_FP ",r0\n\tmov %0, #0" : "=r" (result) : "m" (fp) : "r0"); __asm__ volatile ("" : : : REGS_TO_SAVE); return result; } #endif greenlet-0.4.12/platform/switch_arm32_ios.h000066400000000000000000000035551304443540200205660ustar00rootroot00000000000000/* * this is the internal transfer function. * * HISTORY * 31-May-15 iOS support. Ported from arm32. Proton * * NOTES * * It is not possible to detect if fp is used or not, so the supplied * switch function needs to support it, so that you can remove it if * it does not apply to you. * * POSSIBLE ERRORS * * "fp cannot be used in asm here" * * - Try commenting out "fp" in REGS_TO_SAVE. * */ #define STACK_REFPLUS 1 #ifdef SLP_EVAL #define STACK_MAGIC 0 #define REG_SP "sp" #define REG_SPSP "sp,sp" #define REG_FP "r7" #define REG_FPFP "r7,r7" #define REGS_TO_SAVE_GENERAL "r4", "r5", "r6", "r8", "r10", "r11", "lr" #define REGS_TO_SAVE REGS_TO_SAVE_GENERAL, "d8", "d9", "d10", "d11", \ "d12", "d13", "d14", "d15" static int #ifdef __GNUC__ __attribute__((optimize("no-omit-frame-pointer"))) #endif slp_switch(void) { void *fp; register int *stackref, stsizediff, result; __asm__ volatile ("" : : : REGS_TO_SAVE); __asm__ volatile ("str " REG_FP ",%0" : "=m" (fp)); __asm__ ("mov %0," REG_SP : "=r" (stackref)); { SLP_SAVE_STATE(stackref, stsizediff); __asm__ volatile ( "add " REG_SPSP ",%0\n" "add " REG_FPFP ",%0\n" : : "r" (stsizediff) : REGS_TO_SAVE /* Clobber registers, force compiler to * recalculate address of void *fp from REG_SP or REG_FP */ ); SLP_RESTORE_STATE(); } __asm__ volatile ( "ldr " REG_FP ", %1\n\t" "mov %0, #0" : "=r" (result) : "m" (fp) : REGS_TO_SAVE /* Force compiler to restore saved registers after this */ ); return result; } #endif greenlet-0.4.12/platform/switch_m68k_gcc.h000066400000000000000000000016401304443540200203620ustar00rootroot00000000000000/* * this is the internal transfer function. * * HISTORY * 2014-01-06 Andreas Schwab * File created. */ #ifdef SLP_EVAL #define STACK_MAGIC 0 #define REGS_TO_SAVE "%d2", "%d3", "%d4", "%d5", "%d6", "%d7", \ "%a2", "%a3", "%a4" static int slp_switch(void) { int err; int *stackref, stsizediff; void *fp, *a5; __asm__ volatile ("" : : : REGS_TO_SAVE); __asm__ volatile ("move.l %%fp, %0" : "=m"(fp)); __asm__ volatile ("move.l %%a5, %0" : "=m"(a5)); __asm__ ("move.l %%sp, %0" : "=r"(stackref)); { SLP_SAVE_STATE(stackref, stsizediff); __asm__ volatile ("add.l %0, %%sp; add.l %0, %%fp" : : "r"(stsizediff)); SLP_RESTORE_STATE(); __asm__ volatile ("clr.l %0" : "=g" (err)); } __asm__ volatile ("move.l %0, %%a5" : : "m"(a5)); __asm__ volatile ("move.l %0, %%fp" : : "m"(fp)); __asm__ volatile ("" : : : REGS_TO_SAVE); return err; } #endif greenlet-0.4.12/platform/switch_mips_unix.h000066400000000000000000000026441304443540200210010ustar00rootroot00000000000000/* * this is the internal transfer function. * * HISTORY * 20-Sep-14 Matt Madison * Re-code the saving of the gp register for MIPS64. * 05-Jan-08 Thiemo Seufer * Ported from ppc. */ #define STACK_REFPLUS 1 #ifdef SLP_EVAL #define STACK_MAGIC 0 #define REGS_TO_SAVE "$16", "$17", "$18", "$19", "$20", "$21", "$22", \ "$23", "$30" static int slp_switch(void) { register int err; register int *stackref, stsizediff; #ifdef __mips64 uint64_t gpsave; #endif __asm__ __volatile__ ("" : : : REGS_TO_SAVE); #ifdef __mips64 __asm__ __volatile__ ("sd $28,%0" : "=m" (gpsave) : : ); #endif __asm__ ("move %0, $29" : "=r" (stackref) : ); { SLP_SAVE_STATE(stackref, stsizediff); __asm__ __volatile__ ( #ifdef __mips64 "daddu $29, %0\n" #else "addu $29, %0\n" #endif : /* no outputs */ : "r" (stsizediff) ); SLP_RESTORE_STATE(); } #ifdef __mips64 __asm__ __volatile__ ("ld $28,%0" : : "m" (gpsave) : ); #endif __asm__ __volatile__ ("" : : : REGS_TO_SAVE); __asm__ __volatile__ ("move %0, $0" : "=r" (err)); return err; } #endif /* * further self-processing support */ /* * if you want to add self-inspection tools, place them * here. See the x86_msvc for the necessary defines. * These features are highly experimental und not * essential yet. */ greenlet-0.4.12/platform/switch_ppc64_linux.h000066400000000000000000000057441304443540200211450ustar00rootroot00000000000000/* * this is the internal transfer function. * * HISTORY * 10-Dec-13 Ulrich Weigand * Support ELFv2 ABI. Save float/vector registers. * 09-Mar-12 Michael Ellerman * 64-bit implementation, copied from 32-bit. * 07-Sep-05 (py-dev mailing list discussion) * removed 'r31' from the register-saved. !!!! WARNING !!!! * It means that this file can no longer be compiled statically! * It is now only suitable as part of a dynamic library! * 14-Jan-04 Bob Ippolito * added cr2-cr4 to the registers to be saved. * Open questions: Should we save FP registers? * What about vector registers? * Differences between darwin and unix? * 24-Nov-02 Christian Tismer * needed to add another magic constant to insure * that f in slp_eval_frame(PyFrameObject *f) * STACK_REFPLUS will probably be 1 in most cases. * gets included into the saved stack area. * 04-Oct-02 Gustavo Niemeyer * Ported from MacOS version. * 17-Sep-02 Christian Tismer * after virtualizing stack save/restore, the * stack size shrunk a bit. Needed to introduce * an adjustment STACK_MAGIC per platform. * 15-Sep-02 Gerd Woetzel * slightly changed framework for sparc * 29-Jun-02 Christian Tismer * Added register 13-29, 31 saves. The same way as * Armin Rigo did for the x86_unix version. * This seems to be now fully functional! * 04-Mar-02 Hye-Shik Chang * Ported from i386. * 31-Jul-12 Trevor Bowen * Changed memory constraints to register only. */ #define STACK_REFPLUS 1 #ifdef SLP_EVAL #if _CALL_ELF == 2 #define STACK_MAGIC 4 #else #define STACK_MAGIC 6 #endif #if defined(__ALTIVEC__) #define ALTIVEC_REGS \ "v20", "v21", "v22", "v23", "v24", "v25", "v26", "v27", \ "v28", "v29", "v30", "v31", #else #define ALTIVEC_REGS #endif #define REGS_TO_SAVE "r2", "r14", "r15", "r16", "r17", "r18", "r19", "r20", \ "r21", "r22", "r23", "r24", "r25", "r26", "r27", "r28", "r29", "r31", \ "fr14", "fr15", "fr16", "fr17", "fr18", "fr19", "fr20", "fr21", \ "fr22", "fr23", "fr24", "fr25", "fr26", "fr27", "fr28", "fr29", \ "fr30", "fr31", \ ALTIVEC_REGS \ "cr2", "cr3", "cr4" static int slp_switch(void) { register int err; register long *stackref, stsizediff; __asm__ volatile ("" : : : REGS_TO_SAVE); __asm__ ("mr %0, 1" : "=r" (stackref) : ); { SLP_SAVE_STATE(stackref, stsizediff); __asm__ volatile ( "mr 11, %0\n" "add 1, 1, 11\n" : /* no outputs */ : "r" (stsizediff) : "11" ); SLP_RESTORE_STATE(); } __asm__ volatile ("" : : : REGS_TO_SAVE); __asm__ volatile ("li %0, 0" : "=r" (err)); return err; } #endif greenlet-0.4.12/platform/switch_ppc_aix.h000066400000000000000000000056171304443540200204140ustar00rootroot00000000000000/* * this is the internal transfer function. * * HISTORY * 07-Mar-11 Floris Bruynooghe * Do not add stsizediff to general purpose * register (GPR) 30 as this is a non-volatile and * unused by the PowerOpen Environment, therefore * this was modifying a user register instead of the * frame pointer (which does not seem to exist). * 07-Sep-05 (py-dev mailing list discussion) * removed 'r31' from the register-saved. !!!! WARNING !!!! * It means that this file can no longer be compiled statically! * It is now only suitable as part of a dynamic library! * 14-Jan-04 Bob Ippolito * added cr2-cr4 to the registers to be saved. * Open questions: Should we save FP registers? * What about vector registers? * Differences between darwin and unix? * 24-Nov-02 Christian Tismer * needed to add another magic constant to insure * that f in slp_eval_frame(PyFrameObject *f) * STACK_REFPLUS will probably be 1 in most cases. * gets included into the saved stack area. * 04-Oct-02 Gustavo Niemeyer * Ported from MacOS version. * 17-Sep-02 Christian Tismer * after virtualizing stack save/restore, the * stack size shrunk a bit. Needed to introduce * an adjustment STACK_MAGIC per platform. * 15-Sep-02 Gerd Woetzel * slightly changed framework for sparc * 29-Jun-02 Christian Tismer * Added register 13-29, 31 saves. The same way as * Armin Rigo did for the x86_unix version. * This seems to be now fully functional! * 04-Mar-02 Hye-Shik Chang * Ported from i386. */ #define STACK_REFPLUS 1 #ifdef SLP_EVAL #define STACK_MAGIC 3 /* !!!!WARNING!!!! need to add "r31" in the next line if this header file * is meant to be compiled non-dynamically! */ #define REGS_TO_SAVE "r13", "r14", "r15", "r16", "r17", "r18", "r19", "r20", \ "r21", "r22", "r23", "r24", "r25", "r26", "r27", "r28", "r29", \ "cr2", "cr3", "cr4" static int slp_switch(void) { register int err; register int *stackref, stsizediff; __asm__ volatile ("" : : : REGS_TO_SAVE); __asm__ ("mr %0, 1" : "=g" (stackref) : ); { SLP_SAVE_STATE(stackref, stsizediff); __asm__ volatile ( "mr 11, %0\n" "add 1, 1, 11\n" : /* no outputs */ : "g" (stsizediff) : "11" ); SLP_RESTORE_STATE(); } __asm__ volatile ("" : : : REGS_TO_SAVE); __asm__ volatile ("li %0, 0" : "=r" (err)); return err; } #endif /* * further self-processing support */ /* * if you want to add self-inspection tools, place them * here. See the x86_msvc for the necessary defines. * These features are highly experimental und not * essential yet. */ greenlet-0.4.12/platform/switch_ppc_linux.h000066400000000000000000000053311304443540200207630ustar00rootroot00000000000000/* * this is the internal transfer function. * * HISTORY * 07-Sep-05 (py-dev mailing list discussion) * removed 'r31' from the register-saved. !!!! WARNING !!!! * It means that this file can no longer be compiled statically! * It is now only suitable as part of a dynamic library! * 14-Jan-04 Bob Ippolito * added cr2-cr4 to the registers to be saved. * Open questions: Should we save FP registers? * What about vector registers? * Differences between darwin and unix? * 24-Nov-02 Christian Tismer * needed to add another magic constant to insure * that f in slp_eval_frame(PyFrameObject *f) * STACK_REFPLUS will probably be 1 in most cases. * gets included into the saved stack area. * 04-Oct-02 Gustavo Niemeyer * Ported from MacOS version. * 17-Sep-02 Christian Tismer * after virtualizing stack save/restore, the * stack size shrunk a bit. Needed to introduce * an adjustment STACK_MAGIC per platform. * 15-Sep-02 Gerd Woetzel * slightly changed framework for sparc * 29-Jun-02 Christian Tismer * Added register 13-29, 31 saves. The same way as * Armin Rigo did for the x86_unix version. * This seems to be now fully functional! * 04-Mar-02 Hye-Shik Chang * Ported from i386. * 31-Jul-12 Trevor Bowen * Changed memory constraints to register only. */ #define STACK_REFPLUS 1 #ifdef SLP_EVAL #define STACK_MAGIC 3 /* !!!!WARNING!!!! need to add "r31" in the next line if this header file * is meant to be compiled non-dynamically! */ #define REGS_TO_SAVE "r13", "r14", "r15", "r16", "r17", "r18", "r19", "r20", \ "r21", "r22", "r23", "r24", "r25", "r26", "r27", "r28", "r29", \ "cr2", "cr3", "cr4" static int slp_switch(void) { register int err; register int *stackref, stsizediff; __asm__ volatile ("" : : : REGS_TO_SAVE); __asm__ ("mr %0, 1" : "=r" (stackref) : ); { SLP_SAVE_STATE(stackref, stsizediff); __asm__ volatile ( "mr 11, %0\n" "add 1, 1, 11\n" "add 30, 30, 11\n" : /* no outputs */ : "r" (stsizediff) : "11" ); SLP_RESTORE_STATE(); } __asm__ volatile ("" : : : REGS_TO_SAVE); __asm__ volatile ("li %0, 0" : "=r" (err)); return err; } #endif /* * further self-processing support */ /* * if you want to add self-inspection tools, place them * here. See the x86_msvc for the necessary defines. * These features are highly experimental und not * essential yet. */ greenlet-0.4.12/platform/switch_ppc_macosx.h000066400000000000000000000051221304443540200211140ustar00rootroot00000000000000/* * this is the internal transfer function. * * HISTORY * 07-Sep-05 (py-dev mailing list discussion) * removed 'r31' from the register-saved. !!!! WARNING !!!! * It means that this file can no longer be compiled statically! * It is now only suitable as part of a dynamic library! * 14-Jan-04 Bob Ippolito * added cr2-cr4 to the registers to be saved. * Open questions: Should we save FP registers? * What about vector registers? * Differences between darwin and unix? * 24-Nov-02 Christian Tismer * needed to add another magic constant to insure * that f in slp_eval_frame(PyFrameObject *f) * STACK_REFPLUS will probably be 1 in most cases. * gets included into the saved stack area. * 17-Sep-02 Christian Tismer * after virtualizing stack save/restore, the * stack size shrunk a bit. Needed to introduce * an adjustment STACK_MAGIC per platform. * 15-Sep-02 Gerd Woetzel * slightly changed framework for sparc * 29-Jun-02 Christian Tismer * Added register 13-29, 31 saves. The same way as * Armin Rigo did for the x86_unix version. * This seems to be now fully functional! * 04-Mar-02 Hye-Shik Chang * Ported from i386. */ #define STACK_REFPLUS 1 #ifdef SLP_EVAL #define STACK_MAGIC 3 /* !!!!WARNING!!!! need to add "r31" in the next line if this header file * is meant to be compiled non-dynamically! */ #define REGS_TO_SAVE "r13", "r14", "r15", "r16", "r17", "r18", "r19", "r20", \ "r21", "r22", "r23", "r24", "r25", "r26", "r27", "r28", "r29", \ "cr2", "cr3", "cr4" static int slp_switch(void) { register int err; register int *stackref, stsizediff; __asm__ volatile ("" : : : REGS_TO_SAVE); __asm__ ("; asm block 2\n\tmr %0, r1" : "=g" (stackref) : ); { SLP_SAVE_STATE(stackref, stsizediff); __asm__ volatile ( "; asm block 3\n" "\tmr r11, %0\n" "\tadd r1, r1, r11\n" "\tadd r30, r30, r11\n" : /* no outputs */ : "g" (stsizediff) : "r11" ); SLP_RESTORE_STATE(); } __asm__ volatile ("" : : : REGS_TO_SAVE); __asm__ volatile ("li %0, 0" : "=r" (err)); return err; } #endif /* * further self-processing support */ /* * if you want to add self-inspection tools, place them * here. See the x86_msvc for the necessary defines. * These features are highly experimental und not * essential yet. */ greenlet-0.4.12/platform/switch_ppc_unix.h000066400000000000000000000051561304443540200206140ustar00rootroot00000000000000/* * this is the internal transfer function. * * HISTORY * 07-Sep-05 (py-dev mailing list discussion) * removed 'r31' from the register-saved. !!!! WARNING !!!! * It means that this file can no longer be compiled statically! * It is now only suitable as part of a dynamic library! * 14-Jan-04 Bob Ippolito * added cr2-cr4 to the registers to be saved. * Open questions: Should we save FP registers? * What about vector registers? * Differences between darwin and unix? * 24-Nov-02 Christian Tismer * needed to add another magic constant to insure * that f in slp_eval_frame(PyFrameObject *f) * STACK_REFPLUS will probably be 1 in most cases. * gets included into the saved stack area. * 04-Oct-02 Gustavo Niemeyer * Ported from MacOS version. * 17-Sep-02 Christian Tismer * after virtualizing stack save/restore, the * stack size shrunk a bit. Needed to introduce * an adjustment STACK_MAGIC per platform. * 15-Sep-02 Gerd Woetzel * slightly changed framework for sparc * 29-Jun-02 Christian Tismer * Added register 13-29, 31 saves. The same way as * Armin Rigo did for the x86_unix version. * This seems to be now fully functional! * 04-Mar-02 Hye-Shik Chang * Ported from i386. */ #define STACK_REFPLUS 1 #ifdef SLP_EVAL #define STACK_MAGIC 3 /* !!!!WARNING!!!! need to add "r31" in the next line if this header file * is meant to be compiled non-dynamically! */ #define REGS_TO_SAVE "r13", "r14", "r15", "r16", "r17", "r18", "r19", "r20", \ "r21", "r22", "r23", "r24", "r25", "r26", "r27", "r28", "r29", \ "cr2", "cr3", "cr4" static int slp_switch(void) { register int err; register int *stackref, stsizediff; __asm__ volatile ("" : : : REGS_TO_SAVE); __asm__ ("mr %0, 1" : "=g" (stackref) : ); { SLP_SAVE_STATE(stackref, stsizediff); __asm__ volatile ( "mr 11, %0\n" "add 1, 1, 11\n" "add 30, 30, 11\n" : /* no outputs */ : "g" (stsizediff) : "11" ); SLP_RESTORE_STATE(); } __asm__ volatile ("" : : : REGS_TO_SAVE); __asm__ volatile ("li %0, 0" : "=r" (err)); return err; } #endif /* * further self-processing support */ /* * if you want to add self-inspection tools, place them * here. See the x86_msvc for the necessary defines. * These features are highly experimental und not * essential yet. */ greenlet-0.4.12/platform/switch_s390_unix.h000066400000000000000000000053351304443540200205270ustar00rootroot00000000000000/* * this is the internal transfer function. * * HISTORY * 25-Jan-12 Alexey Borzenkov * Fixed Linux/S390 port to work correctly with * different optimization options both on 31-bit * and 64-bit. Thanks to Stefan Raabe for lots * of testing. * 24-Nov-02 Christian Tismer * needed to add another magic constant to insure * that f in slp_eval_frame(PyFrameObject *f) * STACK_REFPLUS will probably be 1 in most cases. * gets included into the saved stack area. * 06-Oct-02 Gustavo Niemeyer * Ported to Linux/S390. */ #define STACK_REFPLUS 1 #ifdef SLP_EVAL #ifdef __s390x__ #define STACK_MAGIC 20 /* 20 * 8 = 160 bytes of function call area */ #else #define STACK_MAGIC 24 /* 24 * 4 = 96 bytes of function call area */ #endif /* Technically, r11-r13 also need saving, but function prolog starts with stm(g) and since there are so many saved registers already it won't be optimized, resulting in all r6-r15 being saved */ #define REGS_TO_SAVE "r6", "r7", "r8", "r9", "r10", "r14", \ "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", \ "f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15" static int slp_switch(void) { register int ret; register long *stackref, stsizediff; __asm__ volatile ("" : : : REGS_TO_SAVE); #ifdef __s390x__ __asm__ volatile ("lgr %0, 15" : "=r" (stackref) : ); #else __asm__ volatile ("lr %0, 15" : "=r" (stackref) : ); #endif { SLP_SAVE_STATE(stackref, stsizediff); /* N.B. r11 may be used as the frame pointer, and in that case it cannot be clobbered and needs offsetting just like the stack pointer (but in cases where frame pointer isn't used we might clobber it accidentally). What's scary is that r11 is 2nd (and even 1st when GOT is used) callee saved register that gcc would chose for surviving function calls. However, since r6-r10 are clobbered above, their cost for reuse is reduced, so gcc IRA will chose them over r11 (not seeing r11 is implicitly saved), making it relatively safe to offset in all cases. :) */ __asm__ volatile ( #ifdef __s390x__ "agr 15, %0\n\t" "agr 11, %0" #else "ar 15, %0\n\t" "ar 11, %0" #endif : /* no outputs */ : "r" (stsizediff) ); SLP_RESTORE_STATE(); } __asm__ volatile ("" : : : REGS_TO_SAVE); __asm__ volatile ("lhi %0, 0" : "=r" (ret) : ); return ret; } #endif /* * further self-processing support */ /* * if you want to add self-inspection tools, place them * here. See the x86_msvc for the necessary defines. * These features are highly experimental und not * essential yet. */ greenlet-0.4.12/platform/switch_sparc_sun_gcc.h000066400000000000000000000053771304443540200216050ustar00rootroot00000000000000/* * this is the internal transfer function. * * HISTORY * 16-May-15 Alexey Borzenkov * Move stack spilling code inside save/restore functions * 30-Aug-13 Floris Bruynooghe Clean the register windows again before returning. This does not clobber the PIC register as it leaves the current window intact and is required for multi- threaded code to work correctly. * 08-Mar-11 Floris Bruynooghe * No need to set return value register explicitly * before the stack and framepointer are adjusted * as none of the other registers are influenced by * this. Also don't needlessly clean the windows * ('ta %0" :: "i" (ST_CLEAN_WINDOWS)') as that * clobbers the gcc PIC register (%l7). * 24-Nov-02 Christian Tismer * needed to add another magic constant to insure * that f in slp_eval_frame(PyFrameObject *f) * STACK_REFPLUS will probably be 1 in most cases. * gets included into the saved stack area. * 17-Sep-02 Christian Tismer * after virtualizing stack save/restore, the * stack size shrunk a bit. Needed to introduce * an adjustment STACK_MAGIC per platform. * 15-Sep-02 Gerd Woetzel * added support for SunOS sparc with gcc */ #define STACK_REFPLUS 1 #ifdef SLP_EVAL #define STACK_MAGIC 0 #if defined(__sparcv9) #define SLP_FLUSHW __asm__ volatile ("flushw") #else #define SLP_FLUSHW __asm__ volatile ("ta 3") /* ST_FLUSH_WINDOWS */ #endif /* On sparc we need to spill register windows inside save/restore functions */ #define SLP_BEFORE_SAVE_STATE() SLP_FLUSHW #define SLP_BEFORE_RESTORE_STATE() SLP_FLUSHW static int slp_switch(void) { register int err; register int *stackref, stsizediff; /* Put current stack pointer into stackref. * Register spilling is done in save/restore. */ __asm__ volatile ("mov %%sp, %0" : "=r" (stackref)); { /* Thou shalt put SLP_SAVE_STATE into a local block */ /* Copy the current stack onto the heap */ SLP_SAVE_STATE(stackref, stsizediff); /* Increment stack and frame pointer by stsizediff */ __asm__ volatile ( "add %0, %%sp, %%sp\n\t" "add %0, %%fp, %%fp" : : "r" (stsizediff)); /* Copy new stack from it's save store on the heap */ SLP_RESTORE_STATE(); __asm__ volatile ("mov %1, %0" : "=r" (err) : "i" (0)); return err; } } #endif /* * further self-processing support */ /* * if you want to add self-inspection tools, place them * here. See the x86_msvc for the necessary defines. * These features are highly experimental und not * essential yet. */ greenlet-0.4.12/platform/switch_x32_unix.h000066400000000000000000000027671304443540200204530ustar00rootroot00000000000000/* * this is the internal transfer function. * * HISTORY * 17-Aug-12 Fantix King * Ported from amd64. */ #define STACK_REFPLUS 1 #ifdef SLP_EVAL #define STACK_MAGIC 0 #define REGS_TO_SAVE "r12", "r13", "r14", "r15" static int slp_switch(void) { void* ebp; void* ebx; unsigned int csr; unsigned short cw; register int err; register int *stackref, stsizediff; __asm__ volatile ("" : : : REGS_TO_SAVE); __asm__ volatile ("fstcw %0" : "=m" (cw)); __asm__ volatile ("stmxcsr %0" : "=m" (csr)); __asm__ volatile ("movl %%ebp, %0" : "=m" (ebp)); __asm__ volatile ("movl %%ebx, %0" : "=m" (ebx)); __asm__ ("movl %%esp, %0" : "=g" (stackref)); { SLP_SAVE_STATE(stackref, stsizediff); __asm__ volatile ( "addl %0, %%esp\n" "addl %0, %%ebp\n" : : "r" (stsizediff) ); SLP_RESTORE_STATE(); } __asm__ volatile ("movl %0, %%ebx" : : "m" (ebx)); __asm__ volatile ("movl %0, %%ebp" : : "m" (ebp)); __asm__ volatile ("ldmxcsr %0" : : "m" (csr)); __asm__ volatile ("fldcw %0" : : "m" (cw)); __asm__ volatile ("" : : : REGS_TO_SAVE); __asm__ volatile ("xorl %%eax, %%eax" : "=a" (err)); return err; } #endif /* * further self-processing support */ /* * if you want to add self-inspection tools, place them * here. See the x86_msvc for the necessary defines. * These features are highly experimental und not * essential yet. */ greenlet-0.4.12/platform/switch_x64_masm.asm000066400000000000000000000034611304443540200207530ustar00rootroot00000000000000; ; stack switching code for MASM on x641 ; Kristjan Valur Jonsson, sept 2005 ; ;prototypes for our calls slp_save_state_asm PROTO slp_restore_state_asm PROTO pushxmm MACRO reg sub rsp, 16 .allocstack 16 movaps [rsp], reg ; faster than movups, but we must be aligned ; .savexmm128 reg, offset (don't know what offset is, no documentation) ENDM popxmm MACRO reg movaps reg, [rsp] ; faster than movups, but we must be aligned add rsp, 16 ENDM pushreg MACRO reg push reg .pushreg reg ENDM popreg MACRO reg pop reg ENDM .code slp_switch PROC FRAME ;realign stack to 16 bytes after return address push, makes the following faster sub rsp,8 .allocstack 8 pushxmm xmm15 pushxmm xmm14 pushxmm xmm13 pushxmm xmm12 pushxmm xmm11 pushxmm xmm10 pushxmm xmm9 pushxmm xmm8 pushxmm xmm7 pushxmm xmm6 pushreg r15 pushreg r14 pushreg r13 pushreg r12 pushreg rbp pushreg rbx pushreg rdi pushreg rsi sub rsp, 10h ;allocate the singlefunction argument (must be multiple of 16) .allocstack 10h .endprolog lea rcx, [rsp+10h] ;load stack base that we are saving call slp_save_state_asm ;pass stackpointer, return offset in eax cmp rax, 1 je EXIT1 cmp rax, -1 je EXIT2 ;actual stack switch: add rsp, rax call slp_restore_state_asm xor rax, rax ;return 0 EXIT: add rsp, 10h popreg rsi popreg rdi popreg rbx popreg rbp popreg r12 popreg r13 popreg r14 popreg r15 popxmm xmm6 popxmm xmm7 popxmm xmm8 popxmm xmm9 popxmm xmm10 popxmm xmm11 popxmm xmm12 popxmm xmm13 popxmm xmm14 popxmm xmm15 add rsp, 8 ret EXIT1: mov rax, 1 jmp EXIT EXIT2: sar rax, 1 jmp EXIT slp_switch ENDP ENDgreenlet-0.4.12/platform/switch_x64_masm.obj000066400000000000000000000020661304443540200207450ustar00rootroot00000000000000d†ÀëKæ.textÜì P`.data@PÀ.pdata  @0@.xdata,*@@@.debug$SV@BHƒìHƒìD)<$HƒìD)4$HƒìD),$HƒìD)$$HƒìD)$HƒìD)$HƒìD) $HƒìD)$Hƒì)<$Hƒì)4$AWAVAUATUSWVHƒìHL$èHƒø„‚Hƒøÿ„HàèH3ÀHƒÄ^_[]A\A]A^A_(4$HƒÄ(<$HƒÄD($HƒÄD( $HƒÄD($HƒÄD($HƒÄD($$HƒÄD(,$HƒÄD(4$HƒÄD(<$HƒÄHƒÄÃHÇÀëŠHÑøë…r Ž   llh`gpf0ePdÀbÐ`à^ðXPG>5,#ñ‚GD:\Dev\Compile\Greenlet\greenlet-hg\platform\switch_x64_masm.obj7<Ð xMicrosoft (R) Macro Assembler@comp.id x•ÿÿ.text.data.pdata .xdata,.debug$S  - 8Bslp_save_state_asmslp_restore_state_asmslp_switch$xdatasymgreenlet-0.4.12/platform/switch_x64_msvc.h000066400000000000000000000033071304443540200204340ustar00rootroot00000000000000/* * this is the internal transfer function. * * HISTORY * 24-Nov-02 Christian Tismer * needed to add another magic constant to insure * that f in slp_eval_frame(PyFrameObject *f) * STACK_REFPLUS will probably be 1 in most cases. * gets included into the saved stack area. * 26-Sep-02 Christian Tismer * again as a result of virtualized stack access, * the compiler used less registers. Needed to * explicit mention registers in order to get them saved. * Thanks to Jeff Senn for pointing this out and help. * 17-Sep-02 Christian Tismer * after virtualizing stack save/restore, the * stack size shrunk a bit. Needed to introduce * an adjustment STACK_MAGIC per platform. * 15-Sep-02 Gerd Woetzel * slightly changed framework for sparc * 01-Mar-02 Christian Tismer * Initial final version after lots of iterations for i386. */ #define alloca _alloca #define STACK_REFPLUS 1 #define STACK_MAGIC 0 /* Use the generic support for an external assembly language slp_switch function. */ #define EXTERNAL_ASM #ifdef SLP_EVAL /* This always uses the external masm assembly file. */ #endif /* * further self-processing support */ /* we have IsBadReadPtr available, so we can peek at objects */ /* #define STACKLESS_SPY #ifdef IMPLEMENT_STACKLESSMODULE #include "Windows.h" #define CANNOT_READ_MEM(p, bytes) IsBadReadPtr(p, bytes) static int IS_ON_STACK(void*p) { int stackref; intptr_t stackbase = ((intptr_t)&stackref) & 0xfffff000; return (intptr_t)p >= stackbase && (intptr_t)p < stackbase + 0x00100000; } #endif */greenlet-0.4.12/platform/switch_x86_msvc.h000066400000000000000000000046611304443540200204440ustar00rootroot00000000000000/* * this is the internal transfer function. * * HISTORY * 24-Nov-02 Christian Tismer * needed to add another magic constant to insure * that f in slp_eval_frame(PyFrameObject *f) * STACK_REFPLUS will probably be 1 in most cases. * gets included into the saved stack area. * 26-Sep-02 Christian Tismer * again as a result of virtualized stack access, * the compiler used less registers. Needed to * explicit mention registers in order to get them saved. * Thanks to Jeff Senn for pointing this out and help. * 17-Sep-02 Christian Tismer * after virtualizing stack save/restore, the * stack size shrunk a bit. Needed to introduce * an adjustment STACK_MAGIC per platform. * 15-Sep-02 Gerd Woetzel * slightly changed framework for sparc * 01-Mar-02 Christian Tismer * Initial final version after lots of iterations for i386. */ #define alloca _alloca #define STACK_REFPLUS 1 #ifdef SLP_EVAL #define STACK_MAGIC 0 /* Some magic to quell warnings and keep slp_switch() from crashing when built with VC90. Disable global optimizations, and the warning: frame pointer register 'ebp' modified by inline assembly code */ #pragma optimize("g", off) #pragma warning(disable:4731) static int slp_switch(void) { void* seh; register int *stackref, stsizediff; __asm mov eax, fs:[0] __asm mov [seh], eax __asm mov stackref, esp; /* modify EBX, ESI and EDI in order to get them preserved */ __asm mov ebx, ebx; __asm xchg esi, edi; { SLP_SAVE_STATE(stackref, stsizediff); __asm { mov eax, stsizediff add esp, eax add ebp, eax } SLP_RESTORE_STATE(); } __asm mov eax, [seh] __asm mov fs:[0], eax return 0; } /* re-enable ebp warning and global optimizations. */ #pragma optimize("g", on) #pragma warning(default:4731) #endif /* * further self-processing support */ /* we have IsBadReadPtr available, so we can peek at objects */ #define STACKLESS_SPY #ifdef IMPLEMENT_STACKLESSMODULE #include "Windows.h" #define CANNOT_READ_MEM(p, bytes) IsBadReadPtr(p, bytes) static int IS_ON_STACK(void*p) { int stackref; int stackbase = ((int)&stackref) & 0xfffff000; return (int)p >= stackbase && (int)p < stackbase + 0x00100000; } #endif greenlet-0.4.12/platform/switch_x86_unix.h000066400000000000000000000057741304443540200204650ustar00rootroot00000000000000/* * this is the internal transfer function. * * HISTORY * 3-May-13 Ralf Schmitt * Add support for strange GCC caller-save decisions * (ported from switch_aarch64_gcc.h) * 19-Aug-11 Alexey Borzenkov * Correctly save ebp, ebx and cw * 07-Sep-05 (py-dev mailing list discussion) * removed 'ebx' from the register-saved. !!!! WARNING !!!! * It means that this file can no longer be compiled statically! * It is now only suitable as part of a dynamic library! * 24-Nov-02 Christian Tismer * needed to add another magic constant to insure * that f in slp_eval_frame(PyFrameObject *f) * STACK_REFPLUS will probably be 1 in most cases. * gets included into the saved stack area. * 17-Sep-02 Christian Tismer * after virtualizing stack save/restore, the * stack size shrunk a bit. Needed to introduce * an adjustment STACK_MAGIC per platform. * 15-Sep-02 Gerd Woetzel * slightly changed framework for spark * 31-Avr-02 Armin Rigo * Added ebx, esi and edi register-saves. * 01-Mar-02 Samual M. Rushing * Ported from i386. */ #define STACK_REFPLUS 1 #ifdef SLP_EVAL /* #define STACK_MAGIC 3 */ /* the above works fine with gcc 2.96, but 2.95.3 wants this */ #define STACK_MAGIC 0 #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5) # define ATTR_NOCLONE __attribute__((noclone)) #else # define ATTR_NOCLONE #endif static int slp_switch(void) { int err; #ifdef _WIN32 void *seh; #endif void *ebp, *ebx; unsigned short cw; register int *stackref, stsizediff; __asm__ volatile ("" : : : "esi", "edi"); __asm__ volatile ("fstcw %0" : "=m" (cw)); __asm__ volatile ("movl %%ebp, %0" : "=m" (ebp)); __asm__ volatile ("movl %%ebx, %0" : "=m" (ebx)); #ifdef _WIN32 __asm__ volatile ( "movl %%fs:0x0, %%eax\n" "movl %%eax, %0\n" : "=m" (seh) : : "eax"); #endif __asm__ ("movl %%esp, %0" : "=g" (stackref)); { SLP_SAVE_STATE(stackref, stsizediff); __asm__ volatile ( "addl %0, %%esp\n" "addl %0, %%ebp\n" : : "r" (stsizediff) ); SLP_RESTORE_STATE(); __asm__ volatile ("xorl %%eax, %%eax" : "=a" (err)); } #ifdef _WIN32 __asm__ volatile ( "movl %0, %%eax\n" "movl %%eax, %%fs:0x0\n" : : "m" (seh) : "eax"); #endif __asm__ volatile ("movl %0, %%ebx" : : "m" (ebx)); __asm__ volatile ("movl %0, %%ebp" : : "m" (ebp)); __asm__ volatile ("fldcw %0" : : "m" (cw)); __asm__ volatile ("" : : : "esi", "edi"); return err; } #endif /* * further self-processing support */ /* * if you want to add self-inspection tools, place them * here. See the x86_msvc for the necessary defines. * These features are highly experimental und not * essential yet. */ greenlet-0.4.12/run-tests.py000077500000000000000000000027011304443540200157230ustar00rootroot00000000000000#! /usr/bin/env python import sys, os, getopt, struct, unittest from distutils.spawn import spawn build = True verbosity = 2 build_base = None here = os.path.dirname(os.path.abspath(__file__)) os.chdir(here) def bits(): """determine if running on a 32 bit or 64 bit platform """ return struct.calcsize("P") * 8 # -- parse options try: opts, args = getopt.getopt(sys.argv[1:], "nqb:") if args: raise getopt.GetoptError("too many arguments") except getopt.GetoptError: sys.exit("run-tests.py: error: %s" % sys.exc_info()[1]) for o, a in opts: if o == "-q": verbosity = 0 elif o == "-n": build = False elif o == "-b": build_base = a # -- build greenlet if build: if verbosity == 0: cmd = [sys.executable, "setup.py", "-q", "build_ext", "-q", "-i"] else: cmd = [sys.executable, "setup.py", "build_ext", "-i"] spawn(cmd, search_path=0) # -- find greenlet but skip the one in "." if not build: oldpath = sys.path[:] sys.path.remove(here) import greenlet if not build: sys.path[:] = oldpath sys.stdout.write("python %s (%s bit) using greenlet %s from %s\n" % (sys.version.split()[0], bits(), greenlet.__version__, greenlet.__file__)) sys.stdout.flush() # -- run tests from tests import test_collector result = unittest.TextTestRunner(verbosity=verbosity).run(test_collector(build_base)) if result.failures or result.errors: sys.exit(1) greenlet-0.4.12/setup.cfg000066400000000000000000000000321304443540200152160ustar00rootroot00000000000000[sdist] formats=gztar zip greenlet-0.4.12/setup.py000077500000000000000000000075051304443540200151260ustar00rootroot00000000000000#! /usr/bin/env python import sys, os, glob, platform, tempfile, shutil # workaround segfaults on openbsd and RHEL 3 / CentOS 3 . see # https://bitbucket.org/ambroff/greenlet/issue/11/segfault-on-openbsd-i386 # https://github.com/python-greenlet/greenlet/issues/4 # https://github.com/python-greenlet/greenlet/issues/94 if ((sys.platform == "openbsd4" and os.uname()[-1] == "i386") or ("-with-redhat-3." in platform.platform() and platform.machine() == 'i686') or (sys.platform == "sunos5" and os.uname()[-1] == "sun4v") or ("SunOS" in platform.platform() and platform.machine() == "sun4v") or (sys.platform == "linux" and platform.machine() == "ppc")): os.environ["CFLAGS"] = ("%s %s" % (os.environ.get("CFLAGS", ""), "-Os")).lstrip() try: if not (sys.modules.get("setuptools") or "develop" in sys.argv or "upload" in sys.argv or "bdist_egg" in sys.argv or "bdist_wheel" in sys.argv or "test" in sys.argv): raise ImportError() from setuptools import setup, Extension setuptools_args = dict(test_suite='tests.test_collector', zip_safe=False) except ImportError: from distutils.core import setup, Extension setuptools_args = dict() def readfile(filename): f = open(filename) try: return f.read() finally: f.close() def _find_platform_headers(): return glob.glob("platform/switch_*.h") if hasattr(sys, "pypy_version_info"): ext_modules = [] headers = [] else: headers = ['greenlet.h'] if sys.platform == 'win32' and '64 bit' in sys.version: # this works when building with msvc, not with 64 bit gcc # switch_x64_masm.obj can be created with setup_switch_x64_masm.cmd extra_objects = ['platform/switch_x64_masm.obj'] else: extra_objects = [] if sys.platform == 'win32' and os.environ.get('GREENLET_STATIC_RUNTIME') in ('1', 'yes'): extra_compile_args = ['/MT'] elif hasattr(os, 'uname') and os.uname()[4] in ['ppc64el', 'ppc64le']: extra_compile_args = ['-fno-tree-dominator-opts'] else: extra_compile_args = [] ext_modules = [Extension( name='greenlet', sources=['greenlet.c'], extra_objects=extra_objects, extra_compile_args=extra_compile_args, depends=['greenlet.h', 'slp_platformselect.h'] + _find_platform_headers())] from distutils.core import Command from my_build_ext import build_ext setup( name="greenlet", version='0.4.12', description='Lightweight in-process concurrent programming', long_description=readfile("README.rst"), maintainer="Alexey Borzenkov", maintainer_email="snaury@gmail.com", url="https://github.com/python-greenlet/greenlet", license="MIT License", platforms=['any'], headers=headers, ext_modules=ext_modules, cmdclass=dict(build_ext=build_ext), classifiers=[ 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', 'Natural Language :: English', 'Programming Language :: C', 'Programming Language :: Python', 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.4', 'Programming Language :: Python :: 2.5', 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.0', 'Programming Language :: Python :: 3.1', 'Programming Language :: Python :: 3.2', 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Operating System :: OS Independent', 'Topic :: Software Development :: Libraries :: Python Modules'], **setuptools_args) greenlet-0.4.12/setup_switch_x64_masm.cmd000066400000000000000000000002411304443540200203230ustar00rootroot00000000000000call "C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\vcvarsall.bat" amd64 ml64 /nologo /c /Fo platform\switch_x64_masm.obj platform\switch_x64_masm.asm greenlet-0.4.12/slp_platformselect.h000066400000000000000000000050071304443540200174570ustar00rootroot00000000000000/* * Platform Selection for Stackless Python */ #if defined(MS_WIN32) && !defined(MS_WIN64) && defined(_M_IX86) && defined(_MSC_VER) #include "platform/switch_x86_msvc.h" /* MS Visual Studio on X86 */ #elif defined(MS_WIN64) && defined(_M_X64) && defined(_MSC_VER) #include "platform/switch_x64_msvc.h" /* MS Visual Studio on X64 */ #elif defined(__GNUC__) && defined(__amd64__) && defined(__ILP32__) #include "platform/switch_x32_unix.h" /* gcc on amd64 with x32 ABI */ #elif defined(__GNUC__) && defined(__amd64__) #include "platform/switch_amd64_unix.h" /* gcc on amd64 */ #elif defined(__GNUC__) && defined(__i386__) #include "platform/switch_x86_unix.h" /* gcc on X86 */ #elif defined(__GNUC__) && defined(__powerpc64__) && defined(__linux__) #include "platform/switch_ppc64_linux.h" /* gcc on PowerPC 64-bit */ #elif defined(__GNUC__) && defined(__PPC__) && defined(__linux__) #include "platform/switch_ppc_linux.h" /* gcc on PowerPC */ #elif defined(__GNUC__) && defined(__ppc__) && defined(__APPLE__) #include "platform/switch_ppc_macosx.h" /* Apple MacOS X on PowerPC */ #elif defined(__GNUC__) && defined(_ARCH_PPC) && defined(_AIX) #include "platform/switch_ppc_aix.h" /* gcc on AIX/PowerPC */ #elif defined(__GNUC__) && defined(sparc) #include "platform/switch_sparc_sun_gcc.h" /* SunOS sparc with gcc */ #elif defined(__SUNPRO_C) && defined(sparc) && defined(sun) #include "platform/switch_sparc_sun_gcc.h" /* SunStudio on amd64 */ #elif defined(__SUNPRO_C) && defined(__amd64__) && defined(sun) #include "platform/switch_amd64_unix.h" /* SunStudio on amd64 */ #elif defined(__SUNPRO_C) && defined(__i386__) && defined(sun) #include "platform/switch_x86_unix.h" /* SunStudio on x86 */ #elif defined(__GNUC__) && defined(__s390__) && defined(__linux__) #include "platform/switch_s390_unix.h" /* Linux/S390 */ #elif defined(__GNUC__) && defined(__s390x__) && defined(__linux__) #include "platform/switch_s390_unix.h" /* Linux/S390 zSeries (64-bit) */ #elif defined(__GNUC__) && defined(__arm__) #ifdef __APPLE__ #include #endif #if TARGET_OS_IPHONE #include "platform/switch_arm32_ios.h" /* iPhone OS on arm32 */ #else #include "platform/switch_arm32_gcc.h" /* gcc using arm32 */ #endif #elif defined(__GNUC__) && defined(__mips__) && defined(__linux__) #include "platform/switch_mips_unix.h" /* Linux/MIPS */ #elif defined(__GNUC__) && defined(__aarch64__) #include "platform/switch_aarch64_gcc.h" /* Aarch64 ABI */ #elif defined(__GNUC__) && defined(__mc68000__) #include "platform/switch_m68k_gcc.h" /* gcc on m68k */ #endif greenlet-0.4.12/tests/000077500000000000000000000000001304443540200145445ustar00rootroot00000000000000greenlet-0.4.12/tests/__init__.py000066400000000000000000000037461304443540200166670ustar00rootroot00000000000000import os import glob import unittest from distutils.core import setup, Extension TEST_EXTENSIONS = [ Extension('_test_extension', [os.path.join('tests', '_test_extension.c')], include_dirs=[os.path.curdir]), ] if os.environ.get('GREENLET_TEST_CPP', 'yes').lower() not in ('0', 'no', 'false'): TEST_EXTENSIONS_CPP = [ Extension('_test_extension_cpp', [os.path.join('tests', '_test_extension_cpp.cpp')], language="c++", include_dirs=[os.path.curdir]), ] else: TEST_EXTENSIONS_CPP = [] def test_collector(build_base=None): """Collect all tests under the tests directory and return a unittest.TestSuite """ build_test_extensions(build_base) tests_dir = os.path.realpath(os.path.dirname(__file__)) test_module_list = [ 'tests.%s' % os.path.splitext(os.path.basename(t))[0] for t in glob.glob(os.path.join(tests_dir, 'test_*.py'))] if not TEST_EXTENSIONS_CPP: test_module_list.remove('tests.test_cpp') return unittest.TestLoader().loadTestsFromNames(test_module_list) def build_test_extensions(build_base=None): """Because distutils sucks, it just copies the entire contents of the build results dir (e.g. build/lib.linux-i686-2.6) during installation. That means that we can't put any files there that we don't want to distribute. To deal with it, this code will compile test extensions in a separate directory, prepending it to sys.path afterwards. This way testing with multiple Python release and pydebug versions works and test extensions are not distributed. """ if build_base is None: build_base = os.path.join('build', 'tests') from my_build_ext import build_ext setup( options={ 'build': {'build_base': build_base}, }, cmdclass=dict(build_ext=build_ext), script_args=['-q', 'build_ext', '-q'], ext_modules=TEST_EXTENSIONS + TEST_EXTENSIONS_CPP) greenlet-0.4.12/tests/_test_extension.c000066400000000000000000000107571304443540200201340ustar00rootroot00000000000000/* This is a set of functions used by test_extension_interface.py to test the * Greenlet C API. */ #include "../greenlet.h" #ifndef Py_RETURN_NONE #define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None #endif #define TEST_MODULE_NAME "_test_extension" static PyObject * test_switch(PyObject *self, PyObject *greenlet) { PyObject *result = NULL; if (greenlet == NULL || !PyGreenlet_Check(greenlet)) { PyErr_BadArgument(); return NULL; } result = PyGreenlet_Switch((PyGreenlet *) greenlet, NULL, NULL); if (result == NULL) { if (!PyErr_Occurred()) { PyErr_SetString( PyExc_AssertionError, "greenlet.switch() failed for some reason."); } return NULL; } Py_INCREF(result); return result; } static PyObject * test_switch_kwargs(PyObject *self, PyObject *args, PyObject *kwargs) { PyGreenlet *g = NULL; PyObject *result = NULL; PyArg_ParseTuple(args, "O!", &PyGreenlet_Type, &g); if (g == NULL || !PyGreenlet_Check(g)) { PyErr_BadArgument(); return NULL; } result = PyGreenlet_Switch(g, NULL, kwargs); if (result == NULL) { if (!PyErr_Occurred()) { PyErr_SetString( PyExc_AssertionError, "greenlet.switch() failed for some reason."); } return NULL; } Py_XINCREF(result); return result; } static PyObject * test_getcurrent(PyObject *self) { PyGreenlet *g = PyGreenlet_GetCurrent(); if (g == NULL || !PyGreenlet_Check(g) || !PyGreenlet_ACTIVE(g)) { PyErr_SetString( PyExc_AssertionError, "getcurrent() returned an invalid greenlet"); Py_XDECREF(g); return NULL; } Py_DECREF(g); Py_RETURN_NONE; } static PyObject * test_setparent(PyObject *self, PyObject *arg) { PyGreenlet *current; PyGreenlet *greenlet = NULL; if (arg == NULL || !PyGreenlet_Check(arg)) { PyErr_BadArgument(); return NULL; } if ((current = PyGreenlet_GetCurrent()) == NULL) { return NULL; } greenlet = (PyGreenlet *) arg; if (PyGreenlet_SetParent(greenlet, current)) { Py_DECREF(current); return NULL; } Py_DECREF(current); if (PyGreenlet_Switch(greenlet, NULL, NULL) == NULL) { return NULL; } Py_RETURN_NONE; } static PyObject * test_new_greenlet(PyObject *self, PyObject *callable) { PyObject *result = NULL; PyGreenlet *greenlet = PyGreenlet_New(callable, NULL); if (!greenlet) { return NULL; } result = PyGreenlet_Switch(greenlet, NULL, NULL); if (result == NULL) { return NULL; } Py_INCREF(result); return result; } static PyObject * test_raise_dead_greenlet(PyObject *self) { PyErr_SetString(PyExc_GreenletExit, "test GreenletExit exception."); return NULL; } static PyObject * test_raise_greenlet_error(PyObject *self) { PyErr_SetString(PyExc_GreenletError, "test greenlet.error exception"); return NULL; } static PyObject * test_throw(PyObject *self, PyGreenlet *g) { const char msg[] = "take that sucka!"; PyObject *msg_obj = Py_BuildValue("s", msg); PyGreenlet_Throw(g, PyExc_ValueError, msg_obj, NULL); Py_DECREF(msg_obj); Py_RETURN_NONE; } static PyMethodDef test_methods[] = { {"test_switch", (PyCFunction) test_switch, METH_O, "Switch to the provided greenlet sending provided arguments, and \n" "return the results."}, {"test_switch_kwargs", (PyCFunction) test_switch_kwargs, METH_VARARGS | METH_KEYWORDS, "Switch to the provided greenlet sending the provided keyword args."}, {"test_getcurrent", (PyCFunction) test_getcurrent, METH_NOARGS, "Test PyGreenlet_GetCurrent()"}, {"test_setparent", (PyCFunction) test_setparent, METH_O, "Se the parent of the provided greenlet and switch to it."}, {"test_new_greenlet", (PyCFunction) test_new_greenlet, METH_O, "Test PyGreenlet_New()"}, {"test_raise_dead_greenlet", (PyCFunction) test_raise_dead_greenlet, METH_NOARGS, "Just raise greenlet.GreenletExit"}, {"test_raise_greenlet_error", (PyCFunction) test_raise_greenlet_error, METH_NOARGS, "Just raise greenlet.error"}, {"test_throw", (PyCFunction) test_throw, METH_O, "Throw a ValueError at the provided greenlet"}, {NULL, NULL, 0, NULL} }; #if PY_MAJOR_VERSION >= 3 #define INITERROR return NULL static struct PyModuleDef moduledef = { PyModuleDef_HEAD_INIT, TEST_MODULE_NAME, NULL, 0, test_methods, NULL, NULL, NULL, NULL }; PyMODINIT_FUNC PyInit__test_extension(void) #else #define INITERROR return PyMODINIT_FUNC init_test_extension(void) #endif { PyObject *module = NULL; #if PY_MAJOR_VERSION >= 3 module = PyModule_Create(&moduledef); #else module = Py_InitModule(TEST_MODULE_NAME, test_methods); #endif if (module == NULL) { INITERROR; } PyGreenlet_Import(); #if PY_MAJOR_VERSION >= 3 return module; #endif } greenlet-0.4.12/tests/_test_extension_cpp.cpp000066400000000000000000000051301304443540200213230ustar00rootroot00000000000000/* This is a set of functions used to test C++ exceptions are not * broken during greenlet switches */ #include "../greenlet.h" struct exception_t { int depth; exception_t(int depth) : depth(depth) { } }; /* Functions are called via pointers to prevent inlining */ static void (*p_test_exception_throw)(int depth); static PyObject* (*p_test_exception_switch_recurse)(int depth, int left); static void test_exception_throw(int depth) { throw exception_t(depth); } static PyObject* test_exception_switch_recurse(int depth, int left) { if (left > 0) { return p_test_exception_switch_recurse(depth, left - 1); } PyObject* result = NULL; PyGreenlet* self = PyGreenlet_GetCurrent(); if (self == NULL) return NULL; try { PyGreenlet_Switch(self->parent, NULL, NULL); p_test_exception_throw(depth); PyErr_SetString(PyExc_RuntimeError, "throwing C++ exception didn't work"); } catch(exception_t& e) { if (e.depth != depth) PyErr_SetString(PyExc_AssertionError, "depth mismatch"); else result = PyLong_FromLong(depth); } catch(...) { PyErr_SetString(PyExc_RuntimeError, "unexpected C++ exception"); } Py_DECREF(self); return result; } /* test_exception_switch(int depth) * - recurses depth times * - switches to parent inside try/catch block * - throws an exception that (expected to be caught in the same function) * - verifies depth matches (exceptions shouldn't be caught in other greenlets) */ static PyObject * test_exception_switch(PyObject *self, PyObject *args) { int depth; if (!PyArg_ParseTuple(args, "i", &depth)) return NULL; return p_test_exception_switch_recurse(depth, depth); } static PyMethodDef test_methods[] = { {"test_exception_switch", (PyCFunction)&test_exception_switch, METH_VARARGS, "Switches to parent twice, to test exception handling and greenlet switching."}, {NULL, NULL, 0, NULL} }; #if PY_MAJOR_VERSION >= 3 #define INITERROR return NULL static struct PyModuleDef moduledef = { PyModuleDef_HEAD_INIT, "_test_extension_cpp", NULL, 0, test_methods, NULL, NULL, NULL, NULL }; PyMODINIT_FUNC PyInit__test_extension_cpp(void) #else #define INITERROR return PyMODINIT_FUNC init_test_extension_cpp(void) #endif { PyObject *module = NULL; #if PY_MAJOR_VERSION >= 3 module = PyModule_Create(&moduledef); #else module = Py_InitModule("_test_extension_cpp", test_methods); #endif if (module == NULL) { INITERROR; } PyGreenlet_Import(); if (_PyGreenlet_API == NULL) { INITERROR; } p_test_exception_throw = test_exception_throw; p_test_exception_switch_recurse = test_exception_switch_recurse; #if PY_MAJOR_VERSION >= 3 return module; #endif } greenlet-0.4.12/tests/test_cpp.py000066400000000000000000000006231304443540200167400ustar00rootroot00000000000000import unittest import greenlet import _test_extension_cpp class CPPTests(unittest.TestCase): def test_exception_switch(self): greenlets = [] for i in range(4): g = greenlet.greenlet(_test_extension_cpp.test_exception_switch) g.switch(i) greenlets.append(g) for i, g in enumerate(greenlets): self.assertEqual(g.switch(), i) greenlet-0.4.12/tests/test_extension_interface.py000066400000000000000000000045241304443540200222160ustar00rootroot00000000000000import sys import unittest import greenlet import _test_extension class CAPITests(unittest.TestCase): def test_switch(self): self.assertEqual( 50, _test_extension.test_switch(greenlet.greenlet(lambda: 50))) def test_switch_kwargs(self): def foo(x, y): return x * y g = greenlet.greenlet(foo) self.assertEqual(6, _test_extension.test_switch_kwargs(g, x=3, y=2)) def test_setparent(self): def foo(): def bar(): greenlet.getcurrent().parent.switch() # This final switch should go back to the main greenlet, since # the test_setparent() function in the C extension should have # reparented this greenlet. greenlet.getcurrent().parent.switch() raise AssertionError("Should never have reached this code") child = greenlet.greenlet(bar) child.switch() greenlet.getcurrent().parent.switch(child) greenlet.getcurrent().parent.throw( AssertionError("Should never reach this code")) foo_child = greenlet.greenlet(foo).switch() self.assertEqual(None, _test_extension.test_setparent(foo_child)) def test_getcurrent(self): _test_extension.test_getcurrent() def test_new_greenlet(self): self.assertEqual(-15, _test_extension.test_new_greenlet(lambda: -15)) def test_raise_greenlet_dead(self): self.assertRaises( greenlet.GreenletExit, _test_extension.test_raise_dead_greenlet) def test_raise_greenlet_error(self): self.assertRaises( greenlet.error, _test_extension.test_raise_greenlet_error) def test_throw(self): seen = [] def foo(): try: greenlet.getcurrent().parent.switch() except ValueError: seen.append(sys.exc_info()[1]) except greenlet.GreenletExit: raise AssertionError g = greenlet.greenlet(foo) g.switch() _test_extension.test_throw(g) self.assertEqual(len(seen), 1) self.assertTrue( isinstance(seen[0], ValueError), "ValueError was not raised in foo()") self.assertEqual( str(seen[0]), 'take that sucka!', "message doesn't match") greenlet-0.4.12/tests/test_gc.py000066400000000000000000000055141304443540200165530ustar00rootroot00000000000000import gc import sys import unittest import weakref import greenlet class GCTests(unittest.TestCase): def test_dead_circular_ref(self): o = weakref.ref(greenlet.greenlet(greenlet.getcurrent).switch()) gc.collect() self.assertTrue(o() is None) self.assertFalse(gc.garbage, gc.garbage) if greenlet.GREENLET_USE_GC: # These only work with greenlet gc support def test_circular_greenlet(self): class circular_greenlet(greenlet.greenlet): pass o = circular_greenlet() o.self = o o = weakref.ref(o) gc.collect() self.assertTrue(o() is None) self.assertFalse(gc.garbage, gc.garbage) def test_inactive_ref(self): class inactive_greenlet(greenlet.greenlet): def __init__(self): greenlet.greenlet.__init__(self, run=self.run) def run(self): pass o = inactive_greenlet() o = weakref.ref(o) gc.collect() self.assertTrue(o() is None) self.assertFalse(gc.garbage, gc.garbage) def test_finalizer_crash(self): # This test is designed to crash when active greenlets # are made garbage collectable, until the underlying # problem is resolved. How does it work: # - order of object creation is important # - array is created first, so it is moved to unreachable first # - we create a cycle between a greenlet and this array # - we create an object that participates in gc, is only # referenced by a greenlet, and would corrupt gc lists # on destruction, the easiest is to use an object with # a finalizer # - because array is the first object in unreachable it is # cleared first, which causes all references to greenlet # to disappear and causes greenlet to be destroyed, but since # it is still live it causes a switch during gc, which causes # an object with finalizer to be destroyed, which causes stack # corruption and then a crash class object_with_finalizer(object): def __del__(self): pass array = [] parent = greenlet.getcurrent() def greenlet_body(): greenlet.getcurrent().object = object_with_finalizer() try: parent.switch() finally: del greenlet.getcurrent().object g = greenlet.greenlet(greenlet_body) g.array = array array.append(g) g.switch() del array del g greenlet.getcurrent() gc.collect() greenlet-0.4.12/tests/test_generator.py000066400000000000000000000024001304443540200201370ustar00rootroot00000000000000import unittest from greenlet import greenlet class genlet(greenlet): def __init__(self, *args, **kwds): self.args = args self.kwds = kwds def run(self): fn, = self.fn fn(*self.args, **self.kwds) def __iter__(self): return self def __next__(self): self.parent = greenlet.getcurrent() result = self.switch() if self: return result else: raise StopIteration # Hack: Python < 2.6 compatibility next = __next__ def Yield(value): g = greenlet.getcurrent() while not isinstance(g, genlet): if g is None: raise RuntimeError('yield outside a genlet') g = g.parent g.parent.switch(value) def generator(func): class generator(genlet): fn = (func,) return generator # ____________________________________________________________ class GeneratorTests(unittest.TestCase): def test_generator(self): seen = [] def g(n): for i in range(n): seen.append(i) Yield(i) g = generator(g) for k in range(3): for j in g(5): seen.append(j) self.assertEqual(seen, 3 * [0, 0, 1, 1, 2, 2, 3, 3, 4, 4]) greenlet-0.4.12/tests/test_generator_nested.py000066400000000000000000000067731304443540200215220ustar00rootroot00000000000000import unittest from greenlet import greenlet class genlet(greenlet): def __init__(self, *args, **kwds): self.args = args self.kwds = kwds self.child = None def run(self): fn, = self.fn fn(*self.args, **self.kwds) def __iter__(self): return self def set_child(self, child): self.child = child def __next__(self): if self.child: child = self.child while child.child: tmp = child child = child.child tmp.child = None result = child.switch() else: self.parent = greenlet.getcurrent() result = self.switch() if self: return result else: raise StopIteration # Hack: Python < 2.6 compatibility next = __next__ def Yield(value, level=1): g = greenlet.getcurrent() while level != 0: if not isinstance(g, genlet): raise RuntimeError('yield outside a genlet') if level > 1: g.parent.set_child(g) g = g.parent level -= 1 g.switch(value) def Genlet(func): class Genlet(genlet): fn = (func,) return Genlet # ____________________________________________________________ def g1(n, seen): for i in range(n): seen.append(i + 1) yield i def g2(n, seen): for i in range(n): seen.append(i + 1) Yield(i) g2 = Genlet(g2) def nested(i): Yield(i) def g3(n, seen): for i in range(n): seen.append(i + 1) nested(i) g3 = Genlet(g3) def a(n): if n == 0: return for ii in ax(n - 1): Yield(ii) Yield(n) ax = Genlet(a) def perms(l): if len(l) > 1: for e in l: # No syntactical sugar for generator expressions [Yield([e] + p) for p in perms([x for x in l if x != e])] else: Yield(l) perms = Genlet(perms) def gr1(n): for ii in range(1, n): Yield(ii) Yield(ii * ii, 2) gr1 = Genlet(gr1) def gr2(n, seen): for ii in gr1(n): seen.append(ii) gr2 = Genlet(gr2) class NestedGeneratorTests(unittest.TestCase): def test_layered_genlets(self): seen = [] for ii in gr2(5, seen): seen.append(ii) self.assertEqual(seen, [1, 1, 2, 4, 3, 9, 4, 16]) def test_permutations(self): gen_perms = perms(list(range(4))) permutations = list(gen_perms) self.assertEqual(len(permutations), 4 * 3 * 2 * 1) self.assertTrue([0, 1, 2, 3] in permutations) self.assertTrue([3, 2, 1, 0] in permutations) res = [] for ii in zip(perms(list(range(4))), perms(list(range(3)))): res.append(ii) self.assertEqual( res, [([0, 1, 2, 3], [0, 1, 2]), ([0, 1, 3, 2], [0, 2, 1]), ([0, 2, 1, 3], [1, 0, 2]), ([0, 2, 3, 1], [1, 2, 0]), ([0, 3, 1, 2], [2, 0, 1]), ([0, 3, 2, 1], [2, 1, 0])]) # XXX Test to make sure we are working as a generator expression def test_genlet_simple(self): for g in [g1, g2, g3]: seen = [] for k in range(3): for j in g(5, seen): seen.append(j) self.assertEqual(seen, 3 * [1, 0, 2, 1, 3, 2, 4, 3, 5, 4]) def test_genlet_bad(self): try: Yield(10) except RuntimeError: pass def test_nested_genlets(self): seen = [] for ii in ax(5): seen.append(ii) greenlet-0.4.12/tests/test_greenlet.py000066400000000000000000000413011304443540200177610ustar00rootroot00000000000000import gc import sys import time import threading import unittest from greenlet import greenlet try: from abc import ABCMeta, abstractmethod except ImportError: ABCMeta = None abstractmethod = None class SomeError(Exception): pass def fmain(seen): try: greenlet.getcurrent().parent.switch() except: seen.append(sys.exc_info()[0]) raise raise SomeError def send_exception(g, exc): # note: send_exception(g, exc) can be now done with g.throw(exc). # the purpose of this test is to explicitely check the propagation rules. def crasher(exc): raise exc g1 = greenlet(crasher, parent=g) g1.switch(exc) class GreenletTests(unittest.TestCase): def test_simple(self): lst = [] def f(): lst.append(1) greenlet.getcurrent().parent.switch() lst.append(3) g = greenlet(f) lst.append(0) g.switch() lst.append(2) g.switch() lst.append(4) self.assertEqual(lst, list(range(5))) def test_parent_equals_None(self): g = greenlet(parent=None) def test_run_equals_None(self): g = greenlet(run=None) def test_two_children(self): lst = [] def f(): lst.append(1) greenlet.getcurrent().parent.switch() lst.extend([1, 1]) g = greenlet(f) h = greenlet(f) g.switch() self.assertEqual(len(lst), 1) h.switch() self.assertEqual(len(lst), 2) h.switch() self.assertEqual(len(lst), 4) self.assertEqual(h.dead, True) g.switch() self.assertEqual(len(lst), 6) self.assertEqual(g.dead, True) def test_two_recursive_children(self): lst = [] def f(): lst.append(1) greenlet.getcurrent().parent.switch() def g(): lst.append(1) g = greenlet(f) g.switch() lst.append(1) g = greenlet(g) g.switch() self.assertEqual(len(lst), 3) self.assertEqual(sys.getrefcount(g), 2) def test_threads(self): success = [] def f(): self.test_simple() success.append(True) ths = [threading.Thread(target=f) for i in range(10)] for th in ths: th.start() for th in ths: th.join() self.assertEqual(len(success), len(ths)) def test_exception(self): seen = [] g1 = greenlet(fmain) g2 = greenlet(fmain) g1.switch(seen) g2.switch(seen) g2.parent = g1 self.assertEqual(seen, []) self.assertRaises(SomeError, g2.switch) self.assertEqual(seen, [SomeError]) g2.switch() self.assertEqual(seen, [SomeError]) def test_send_exception(self): seen = [] g1 = greenlet(fmain) g1.switch(seen) self.assertRaises(KeyError, send_exception, g1, KeyError) self.assertEqual(seen, [KeyError]) def test_dealloc(self): seen = [] g1 = greenlet(fmain) g2 = greenlet(fmain) g1.switch(seen) g2.switch(seen) self.assertEqual(seen, []) del g1 gc.collect() self.assertEqual(seen, [greenlet.GreenletExit]) del g2 gc.collect() self.assertEqual(seen, [greenlet.GreenletExit, greenlet.GreenletExit]) def test_dealloc_other_thread(self): seen = [] someref = [] lock = threading.Lock() lock.acquire() lock2 = threading.Lock() lock2.acquire() def f(): g1 = greenlet(fmain) g1.switch(seen) someref.append(g1) del g1 gc.collect() lock.release() lock2.acquire() greenlet() # trigger release lock.release() lock2.acquire() t = threading.Thread(target=f) t.start() lock.acquire() self.assertEqual(seen, []) self.assertEqual(len(someref), 1) del someref[:] gc.collect() # g1 is not released immediately because it's from another thread self.assertEqual(seen, []) lock2.release() lock.acquire() self.assertEqual(seen, [greenlet.GreenletExit]) lock2.release() t.join() def test_frame(self): def f1(): f = sys._getframe(0) self.assertEqual(f.f_back, None) greenlet.getcurrent().parent.switch(f) return "meaning of life" g = greenlet(f1) frame = g.switch() self.assertTrue(frame is g.gr_frame) self.assertTrue(g) next = g.switch() self.assertFalse(g) self.assertEqual(next, 'meaning of life') self.assertEqual(g.gr_frame, None) def test_thread_bug(self): def runner(x): g = greenlet(lambda: time.sleep(x)) g.switch() t1 = threading.Thread(target=runner, args=(0.2,)) t2 = threading.Thread(target=runner, args=(0.3,)) t1.start() t2.start() t1.join() t2.join() def test_switch_kwargs(self): def foo(a, b): self.assertEqual(a, 4) self.assertEqual(b, 2) greenlet(foo).switch(a=4, b=2) def test_switch_kwargs_to_parent(self): def foo(x): greenlet.getcurrent().parent.switch(x=x) greenlet.getcurrent().parent.switch(2, x=3) return x, x ** 2 g = greenlet(foo) self.assertEqual({'x': 3}, g.switch(3)) self.assertEqual(((2,), {'x': 3}), g.switch()) self.assertEqual((3, 9), g.switch()) def test_switch_to_another_thread(self): data = {} error = None created_event = threading.Event() done_event = threading.Event() def foo(): data['g'] = greenlet(lambda: None) created_event.set() done_event.wait() thread = threading.Thread(target=foo) thread.start() created_event.wait() try: data['g'].switch() except greenlet.error: error = sys.exc_info()[1] self.assertTrue(error != None, "greenlet.error was not raised!") done_event.set() thread.join() def test_exc_state(self): def f(): try: raise ValueError('fun') except: exc_info = sys.exc_info() greenlet(h).switch() self.assertEqual(exc_info, sys.exc_info()) def h(): self.assertEqual(sys.exc_info(), (None, None, None)) greenlet(f).switch() def test_instance_dict(self): def f(): greenlet.getcurrent().test = 42 def deldict(g): del g.__dict__ def setdict(g, value): g.__dict__ = value g = greenlet(f) self.assertEqual(g.__dict__, {}) g.switch() self.assertEqual(g.test, 42) self.assertEqual(g.__dict__, {'test': 42}) g.__dict__ = g.__dict__ self.assertEqual(g.__dict__, {'test': 42}) self.assertRaises(TypeError, deldict, g) self.assertRaises(TypeError, setdict, g, 42) def test_threaded_reparent(self): data = {} created_event = threading.Event() done_event = threading.Event() def foo(): data['g'] = greenlet(lambda: None) created_event.set() done_event.wait() def blank(): greenlet.getcurrent().parent.switch() def setparent(g, value): g.parent = value thread = threading.Thread(target=foo) thread.start() created_event.wait() g = greenlet(blank) g.switch() self.assertRaises(ValueError, setparent, g, data['g']) done_event.set() thread.join() def test_deepcopy(self): import copy self.assertRaises(TypeError, copy.copy, greenlet()) self.assertRaises(TypeError, copy.deepcopy, greenlet()) def test_parent_restored_on_kill(self): hub = greenlet(lambda: None) main = greenlet.getcurrent() result = [] def worker(): try: # Wait to be killed main.switch() except greenlet.GreenletExit: # Resurrect and switch to parent result.append(greenlet.getcurrent().parent) result.append(greenlet.getcurrent()) hub.switch() g = greenlet(worker, parent=hub) g.switch() del g self.assertTrue(result) self.assertEqual(result[0], main) self.assertEqual(result[1].parent, hub) def test_parent_return_failure(self): # No run causes AttributeError on switch g1 = greenlet() # Greenlet that implicitly switches to parent g2 = greenlet(lambda: None, parent=g1) # AttributeError should propagate to us, no fatal errors self.assertRaises(AttributeError, g2.switch) def test_throw_exception_not_lost(self): class mygreenlet(greenlet): def __getattribute__(self, name): try: raise Exception() except: pass return greenlet.__getattribute__(self, name) g = mygreenlet(lambda: None) self.assertRaises(SomeError, g.throw, SomeError()) def test_throw_doesnt_crash(self): result = [] def worker(): greenlet.getcurrent().parent.switch() def creator(): g = greenlet(worker) g.switch() result.append(g) t = threading.Thread(target=creator) t.start() t.join() self.assertRaises(greenlet.error, result[0].throw, SomeError()) def test_recursive_startup(self): class convoluted(greenlet): def __init__(self): greenlet.__init__(self) self.count = 0 def __getattribute__(self, name): if name == 'run' and self.count == 0: self.count = 1 self.switch(43) return greenlet.__getattribute__(self, name) def run(self, value): while True: self.parent.switch(value) g = convoluted() self.assertEqual(g.switch(42), 43) def test_unexpected_reparenting(self): another = [] def worker(): g = greenlet(lambda: None) another.append(g) g.switch() t = threading.Thread(target=worker) t.start() t.join() class convoluted(greenlet): def __getattribute__(self, name): if name == 'run': self.parent = another[0] return greenlet.__getattribute__(self, name) g = convoluted(lambda: None) self.assertRaises(greenlet.error, g.switch) def test_threaded_updatecurrent(self): # released when main thread should execute lock1 = threading.Lock() lock1.acquire() # released when another thread should execute lock2 = threading.Lock() lock2.acquire() class finalized(object): def __del__(self): # happens while in green_updatecurrent() in main greenlet # should be very careful not to accidentally call it again # at the same time we must make sure another thread executes lock2.release() lock1.acquire() # now ts_current belongs to another thread def deallocator(): greenlet.getcurrent().parent.switch() def fthread(): lock2.acquire() greenlet.getcurrent() del g[0] lock1.release() lock2.acquire() greenlet.getcurrent() lock1.release() main = greenlet.getcurrent() g = [greenlet(deallocator)] g[0].bomb = finalized() g[0].switch() t = threading.Thread(target=fthread) t.start() # let another thread grab ts_current and deallocate g[0] lock2.release() lock1.acquire() # this is the corner stone # getcurrent() will notice that ts_current belongs to another thread # and start the update process, which would notice that g[0] should # be deallocated, and that will execute an object's finalizer. Now, # that object will let another thread run so it can grab ts_current # again, which would likely crash the interpreter if there's no # check for this case at the end of green_updatecurrent(). This test # passes if getcurrent() returns correct result, but it's likely # to randomly crash if it's not anyway. self.assertEqual(greenlet.getcurrent(), main) # wait for another thread to complete, just in case t.join() def test_dealloc_switch_args_not_lost(self): seen = [] def worker(): # wait for the value value = greenlet.getcurrent().parent.switch() # delete all references to ourself del worker[0] initiator.parent = greenlet.getcurrent().parent # switch to main with the value, but because # ts_current is the last reference to us we # return immediately try: greenlet.getcurrent().parent.switch(value) finally: seen.append(greenlet.getcurrent()) def initiator(): return 42 # implicitly falls thru to parent worker = [greenlet(worker)] worker[0].switch() # prime worker initiator = greenlet(initiator, worker[0]) value = initiator.switch() self.assertTrue(seen) self.assertEqual(value, 42) if sys.version_info[0] == 2: # There's no apply in Python 3.x def test_tuple_subclass(self): class mytuple(tuple): def __len__(self): greenlet.getcurrent().switch() return tuple.__len__(self) args = mytuple() kwargs = dict(a=42) def switchapply(): apply(greenlet.getcurrent().parent.switch, args, kwargs) g = greenlet(switchapply) self.assertEqual(g.switch(), kwargs) if ABCMeta is not None and abstractmethod is not None: def test_abstract_subclasses(self): AbstractSubclass = ABCMeta( 'AbstractSubclass', (greenlet,), {'run': abstractmethod(lambda self: None)}) class BadSubclass(AbstractSubclass): pass class GoodSubclass(AbstractSubclass): def run(self): pass GoodSubclass() # should not raise self.assertRaises(TypeError, BadSubclass) def test_implicit_parent_with_threads(self): if not gc.isenabled(): return # cannot test with disabled gc N = gc.get_threshold()[0] if N < 50: return # cannot test with such a small N def attempt(): lock1 = threading.Lock() lock1.acquire() lock2 = threading.Lock() lock2.acquire() recycled = [False] def another_thread(): lock1.acquire() # wait for gc greenlet.getcurrent() # update ts_current lock2.release() # release gc t = threading.Thread(target=another_thread) t.start() class gc_callback(object): def __del__(self): lock1.release() lock2.acquire() recycled[0] = True class garbage(object): def __init__(self): self.cycle = self self.callback = gc_callback() l = [] x = range(N*2) current = greenlet.getcurrent() g = garbage() for i in x: g = None # lose reference to garbage if recycled[0]: # gc callback called prematurely t.join() return False last = greenlet() if recycled[0]: break # yes! gc called in green_new l.append(last) # increase allocation counter else: # gc callback not called when expected gc.collect() if recycled[0]: t.join() return False self.assertEqual(last.parent, current) for g in l: self.assertEqual(g.parent, current) return True for i in range(5): if attempt(): break greenlet-0.4.12/tests/test_leaks.py000066400000000000000000000057241304443540200172640ustar00rootroot00000000000000import unittest import sys import gc import time import weakref import greenlet import threading class ArgRefcountTests(unittest.TestCase): def test_arg_refs(self): args = ('a', 'b', 'c') refcount_before = sys.getrefcount(args) g = greenlet.greenlet( lambda *args: greenlet.getcurrent().parent.switch(*args)) for i in range(100): g.switch(*args) self.assertEqual(sys.getrefcount(args), refcount_before) def test_kwarg_refs(self): kwargs = {} g = greenlet.greenlet( lambda **kwargs: greenlet.getcurrent().parent.switch(**kwargs)) for i in range(100): g.switch(**kwargs) self.assertEqual(sys.getrefcount(kwargs), 2) if greenlet.GREENLET_USE_GC: # These only work with greenlet gc support def recycle_threads(self): # By introducing a thread that does sleep we allow other threads, # that have triggered their __block condition, but did not have a # chance to deallocate their thread state yet, to finally do so. # The way it works is by requring a GIL switch (different thread), # which does a GIL release (sleep), which might do a GIL switch # to finished threads and allow them to clean up. def worker(): time.sleep(0.001) t = threading.Thread(target=worker) t.start() time.sleep(0.001) t.join() def test_threaded_leak(self): gg = [] def worker(): # only main greenlet present gg.append(weakref.ref(greenlet.getcurrent())) for i in range(2): t = threading.Thread(target=worker) t.start() t.join() del t greenlet.getcurrent() # update ts_current self.recycle_threads() greenlet.getcurrent() # update ts_current gc.collect() greenlet.getcurrent() # update ts_current for g in gg: self.assertTrue(g() is None) def test_threaded_adv_leak(self): gg = [] def worker(): # main and additional *finished* greenlets ll = greenlet.getcurrent().ll = [] def additional(): ll.append(greenlet.getcurrent()) for i in range(2): greenlet.greenlet(additional).switch() gg.append(weakref.ref(greenlet.getcurrent())) for i in range(2): t = threading.Thread(target=worker) t.start() t.join() del t greenlet.getcurrent() # update ts_current self.recycle_threads() greenlet.getcurrent() # update ts_current gc.collect() greenlet.getcurrent() # update ts_current for g in gg: self.assertTrue(g() is None) greenlet-0.4.12/tests/test_stack_saved.py000066400000000000000000000007021304443540200204430ustar00rootroot00000000000000import greenlet import unittest class Test(unittest.TestCase): def test_stack_saved(self): main = greenlet.getcurrent() self.assertEqual(main._stack_saved, 0) def func(): main.switch(main._stack_saved) g = greenlet.greenlet(func) x = g.switch() assert x > 0, x assert g._stack_saved > 0, g._stack_saved g.switch() assert g._stack_saved == 0, g._stack_saved greenlet-0.4.12/tests/test_throw.py000066400000000000000000000053031304443540200173210ustar00rootroot00000000000000import sys import unittest from greenlet import greenlet def switch(*args): return greenlet.getcurrent().parent.switch(*args) class ThrowTests(unittest.TestCase): def test_class(self): def f(): try: switch("ok") except RuntimeError: switch("ok") return switch("fail") g = greenlet(f) res = g.switch() self.assertEqual(res, "ok") res = g.throw(RuntimeError) self.assertEqual(res, "ok") def test_val(self): def f(): try: switch("ok") except RuntimeError: val = sys.exc_info()[1] if str(val) == "ciao": switch("ok") return switch("fail") g = greenlet(f) res = g.switch() self.assertEqual(res, "ok") res = g.throw(RuntimeError("ciao")) self.assertEqual(res, "ok") g = greenlet(f) res = g.switch() self.assertEqual(res, "ok") res = g.throw(RuntimeError, "ciao") self.assertEqual(res, "ok") def test_kill(self): def f(): switch("ok") switch("fail") g = greenlet(f) res = g.switch() self.assertEqual(res, "ok") res = g.throw() self.assertTrue(isinstance(res, greenlet.GreenletExit)) self.assertTrue(g.dead) res = g.throw() # immediately eaten by the already-dead greenlet self.assertTrue(isinstance(res, greenlet.GreenletExit)) def test_throw_goes_to_original_parent(self): main = greenlet.getcurrent() def f1(): try: main.switch("f1 ready to catch") except IndexError: return "caught" else: return "normal exit" def f2(): main.switch("from f2") g1 = greenlet(f1) g2 = greenlet(f2, parent=g1) self.assertRaises(IndexError, g2.throw, IndexError) self.assertTrue(g2.dead) self.assertTrue(g1.dead) g1 = greenlet(f1) g2 = greenlet(f2, parent=g1) res = g1.switch() self.assertEqual(res, "f1 ready to catch") res = g2.throw(IndexError) self.assertEqual(res, "caught") self.assertTrue(g2.dead) self.assertTrue(g1.dead) g1 = greenlet(f1) g2 = greenlet(f2, parent=g1) res = g1.switch() self.assertEqual(res, "f1 ready to catch") res = g2.switch() self.assertEqual(res, "from f2") res = g2.throw(IndexError) self.assertEqual(res, "caught") self.assertTrue(g2.dead) self.assertTrue(g1.dead) greenlet-0.4.12/tests/test_tracing.py000066400000000000000000000031571304443540200176120ustar00rootroot00000000000000import unittest import threading import greenlet class SomeError(Exception): pass class TracingTests(unittest.TestCase): if greenlet.GREENLET_USE_TRACING: def test_greenlet_tracing(self): main = greenlet.getcurrent() actions = [] def trace(*args): actions.append(args) def dummy(): pass def dummyexc(): raise SomeError() oldtrace = greenlet.settrace(trace) try: g1 = greenlet.greenlet(dummy) g1.switch() g2 = greenlet.greenlet(dummyexc) self.assertRaises(SomeError, g2.switch) finally: greenlet.settrace(oldtrace) self.assertEqual(actions, [ ('switch', (main, g1)), ('switch', (g1, main)), ('switch', (main, g2)), ('throw', (g2, main)), ]) def test_exception_disables_tracing(self): main = greenlet.getcurrent() actions = [] def trace(*args): actions.append(args) raise SomeError() def dummy(): main.switch() g = greenlet.greenlet(dummy) g.switch() oldtrace = greenlet.settrace(trace) try: self.assertRaises(SomeError, g.switch) self.assertEqual(greenlet.gettrace(), None) finally: greenlet.settrace(oldtrace) self.assertEqual(actions, [ ('switch', (main, g)), ]) greenlet-0.4.12/tests/test_version.py000077500000000000000000000012151304443540200176440ustar00rootroot00000000000000#! /usr/bin/env python import sys, os, re, unittest, greenlet def readclose(f): try: return f.read() finally: f.close() def readfile(filename): return readclose(open(filename)) class VersionTests(unittest.TestCase): def test_version(self): upfile = lambda p: os.path.join(os.path.dirname(__file__), "..", p) hversion, = re.findall('GREENLET_VERSION "(.*)"', readfile(upfile("greenlet.h"))) sversion = readclose(os.popen("%s %s --version" % (sys.executable, upfile("setup.py")))).strip() self.assertFalse(sversion != hversion) self.assertFalse(sversion != greenlet.__version__) greenlet-0.4.12/tests/test_weakref.py000066400000000000000000000015641304443540200176070ustar00rootroot00000000000000import gc import greenlet import weakref import unittest class WeakRefTests(unittest.TestCase): def test_dead_weakref(self): def _dead_greenlet(): g = greenlet.greenlet(lambda: None) g.switch() return g o = weakref.ref(_dead_greenlet()) gc.collect() self.assertEqual(o(), None) def test_inactive_weakref(self): o = weakref.ref(greenlet.greenlet()) gc.collect() self.assertEqual(o(), None) def test_dealloc_weakref(self): seen = [] def worker(): try: greenlet.getcurrent().parent.switch() finally: seen.append(g()) g = greenlet.greenlet(worker) g.switch() g2 = greenlet.greenlet(lambda: None, g) g = weakref.ref(g2) g2 = None self.assertEqual(seen, [None]) greenlet-0.4.12/tox.ini000066400000000000000000000017371304443540200147250ustar00rootroot00000000000000[testenv] commands={envpython} run-tests.py -nq sitepackages=False # this is 32 bit only [testenv:x-py25] basepython=/usr/bin/python2.5 # test both 32 bit and 64 bit on OS X [testenv:x-py26] commands=/usr/bin/arch -i386 {envpython} run-tests.py -qn /usr/bin/arch -x86_64 {envpython} run-tests.py -qn basepython=/usr/bin/python2.6 [testenv:x-py27] commands=/usr/bin/arch -i386 {envpython} run-tests.py -qn /usr/bin/arch -x86_64 {envpython} run-tests.py -qn basepython=/usr/bin/python2.7 # test with specific gcc version on OS X [testenv:x-py26-gcc40] commands=/usr/bin/arch -i386 {envpython} run-tests.py -qn /usr/bin/arch -x86_64 {envpython} run-tests.py -qn basepython=/usr/bin/python2.6 setenv= CC=gcc-4.0 [testenv:x-py26-gcc42] commands=/usr/bin/arch -i386 {envpython} run-tests.py -qn /usr/bin/arch -x86_64 {envpython} run-tests.py -qn basepython=/usr/bin/python2.6 setenv= CC=gcc-4.2 [testenv:docs] deps=docutils sphinx commands=make html changedir={toxinidir}/doc