Commit 9c8e01d8 authored by Jason Madden's avatar Jason Madden

Compile greenlet with Cython to make up for most of the lost speed.

We're now only ~2x slower, instead of 10x.
parent 15fc1ecc
......@@ -9,6 +9,7 @@ gevent.*.[ch]
src/gevent/__pycache__
src/gevent/_semaphore.c
src/gevent/local.c
src/gevent/greenlet.c
src/gevent/libev/corecext.c
src/gevent/libev/corecext.h
src/gevent/libev/_corecffi.c
......
......@@ -90,6 +90,9 @@
a "spawn tree local" mapping. Based on a proposal from PayPal and
comments by Mahmoud Hashemi and Kurt Rose. See :issue:`755`.
- The :mod:`gevent.greenlet` module is now compiled with Cython to
offset any performance loss due to :issue:`755`.
1.3a1 (2018-01-27)
==================
......
......@@ -223,7 +223,6 @@ latex_documents = [
# prevent some stuff from showing up in docs
import socket
import gevent.socket
del gevent.Greenlet.throw
for item in gevent.socket.__all__[:]:
if getattr(gevent.socket, item) is getattr(socket, item, None):
gevent.socket.__all__.remove(item)
......@@ -36,25 +36,7 @@ generated.
.. automethod:: Greenlet.__init__
.. attribute:: Greenlet.value
Holds the value returned by the function if the greenlet has
finished successfully. Until then, or if it finished in error, ``None``.
.. tip:: Recall that a greenlet killed with the default
:class:`GreenletExit` is considered to have finished
successfully, and the ``GreenletExit`` exception will be
its value.
.. autoattribute:: Greenlet.exception
.. autoattribute:: Greenlet.spawn_tree_locals
:annotation: = {}
.. autoattribute:: Greenlet.spawning_greenlet
:annotation: = weakref.ref()
.. autoattribute:: Greenlet.spawning_stack
:annotation: = <Frame>
.. autoattribute:: Greenlet.spawning_stack_limit
.. automethod:: Greenlet.ready
.. automethod:: Greenlet.successful
.. automethod:: Greenlet.start
......
......@@ -4,6 +4,7 @@ from __future__ import print_function
import sys
import os
import os.path
import sysconfig
# setuptools is *required* on Windows
# (https://bugs.python.org/issue23246) and for PyPy. No reason not to
......@@ -59,11 +60,28 @@ LOCAL = Extension(name="gevent.local",
depends=['src/gevent/local.pxd'])
LOCAL = cythonize1(LOCAL)
# The sysconfig dir is not enough if we're in a virtualenv
# See https://github.com/pypa/pip/issues/4610
include_dirs = [sysconfig.get_path("include")]
venv_include_dir = os.path.join(sys.prefix, 'include', 'site',
'python' + sysconfig.get_python_version())
venv_include_dir = os.path.abspath(venv_include_dir)
if os.path.exists(venv_include_dir):
include_dirs.append(venv_include_dir)
GREENLET = Extension(name="gevent.greenlet",
sources=["src/gevent/greenlet.py"],
depends=['src/gevent/greenlet.pxd'],
include_dirs=include_dirs)
GREENLET = cythonize1(GREENLET)
EXT_MODULES = [
CORE,
ARES,
SEMAPHORE,
LOCAL,
GREENLET,
]
LIBEV_CFFI_MODULE = 'src/gevent/libev/_corecffi_build.py:ffi'
......@@ -91,6 +109,7 @@ if PYPY:
setup_requires = []
EXT_MODULES.remove(CORE)
EXT_MODULES.remove(LOCAL)
EXT_MODULES.remove(GREENLET)
EXT_MODULES.remove(SEMAPHORE)
# By building the semaphore with Cython under PyPy, we get
# atomic operations (specifically, exiting/releasing), at the
......
# cython: auto_pickle=False
cimport cython
cdef extern from "greenlet/greenlet.h":
ctypedef class greenlet.greenlet [object PyGreenlet]:
pass
cdef class SpawnedLink:
cdef public object callback
@cython.final
cdef class SuccessSpawnedLink(SpawnedLink):
pass
@cython.final
cdef class FailureSpawnedLink(SpawnedLink):
pass
@cython.final
@cython.internal
cdef class _Frame:
cdef readonly object f_code
cdef readonly int f_lineno
cdef public _Frame f_back
cdef class Greenlet(greenlet):
cdef readonly object value
cdef readonly args
cdef readonly object spawning_greenlet
cdef public dict spawn_tree_locals
cdef readonly _Frame spawning_stack
# A test case reads these, otherwise they would
# be private
cdef readonly tuple _exc_info
cdef readonly list _links
cdef object _notifier
cdef object _start_event
cdef dict _kwargs
cdef bint __started_but_aborted(self)
cdef bint __start_cancelled_by_kill(self)
cdef bint __start_pending(self)
cdef bint __never_started_or_killed(self)
cdef bint __start_completed(self)
cdef __cancel_start(self)
cdef _report_result(self, object result)
cdef _report_error(self, tuple exc_info)
@cython.final
@cython.internal
cdef class _dummy_event:
cdef readonly bint pending
cdef readonly bint active
cpdef stop(self)
cpdef start(self, cb)
cpdef close(self)
cdef _dummy_event _cancelled_start_event
cdef _dummy_event _start_completed_event
@cython.locals(diehards=list)
cdef _killall3(list greenlets, object exception, object waiter)
cdef _killall(list greenlets, object exception)
@cython.locals(done=list)
cpdef joinall(greenlets, timeout=*, raise_error=*, count=*)
This diff is collapsed.
......@@ -374,7 +374,8 @@ class TestStuff(greentest.TestCase):
link(results.listener2)
link(results.listener3)
sleep(DELAY * 10)
assert results.results == [5], results.results
self.assertEqual([5], results.results)
def test_multiple_listeners_error_unlink_Greenlet_link(self):
p = gevent.spawn(lambda: 5)
......@@ -515,14 +516,14 @@ class TestBasic(greentest.TestCase):
assert g.exception is None
gevent.sleep(0.001)
assert g
assert not g.dead
assert g.started
assert not g.ready()
assert not g.successful()
assert g.value is None
assert g.exception is None
assert not link_test
self.assertTrue(g)
self.assertFalse(g.dead, g)
self.assertTrue(g.started, g)
self.assertFalse(g.ready(), g)
self.assertFalse(g.successful(), g)
self.assertIsNone(g.value, g)
self.assertIsNone(g.exception, g)
self.assertFalse(link_test)
gevent.sleep(0.02)
assert not g
......
......@@ -11,7 +11,7 @@ import greentest
import gevent
from gevent import util
@greentest.skipOnPyPy("5.10.x is *very* slow formatting stacks")
class TestFormat(greentest.TestCase):
def test_basic(self):
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment