Skip to content

Commit

Permalink
Compile greenlet with Cython to make up for most of the lost speed.
Browse files Browse the repository at this point in the history
We're now only ~2x slower, instead of 10x.
  • Loading branch information
jamadden committed Feb 22, 2018
1 parent 15fc1ec commit 9c8e01d
Show file tree
Hide file tree
Showing 9 changed files with 248 additions and 126 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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)
==================

Expand Down
1 change: 0 additions & 1 deletion doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,6 @@
# 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)
18 changes: 0 additions & 18 deletions doc/gevent.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
19 changes: 19 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
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
Expand Down Expand Up @@ -59,11 +60,28 @@
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'
Expand Down Expand Up @@ -91,6 +109,7 @@
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
Expand Down
80 changes: 80 additions & 0 deletions src/gevent/greenlet.pxd
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# 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=*)
Loading

0 comments on commit 9c8e01d

Please sign in to comment.