Commit 3fa1038f authored by Jason Madden's avatar Jason Madden

Fixes #618: Be careful to avoid a recursive import under PyPy that can lead to...

Fixes #618: Be careful to avoid a recursive import under PyPy that can lead to an interpreter crash.

No additional tests needed, because the removal of try/except in
builtins.py would cause existing tests to fail if we still recursed.

As a side-effect, Timeout objects that can never expire should be
cheaper to create now.

See also https://bitbucket.org/pypy/pypy/issues/2089/crash-in-pypy-260-linux64-with-gevent-11b1
parent fee898a3
# This file is renamed to "Makefile.ext" in release tarballs so that setup.py won't try to
# run it. If you want setup.py to run "make" automatically, rename it back to "Makefile".
PYTHON ?= python${TRAVIS_PYTHON_VERSION}
CYTHON ?= cython
all: gevent/gevent.corecext.c gevent/gevent.ares.c gevent/gevent._semaphore.c gevent/gevent._util.c
gevent/gevent.corecext.c: gevent/core.ppyx gevent/libev.pxd
$(PYTHON) util/cythonpp.py -o gevent.corecext.c gevent/core.ppyx
echo >> gevent.corecext.c
echo '#include "callbacks.c"' >> gevent.corecext.c
mv gevent.corecext.* gevent/
gevent/gevent.ares.c: gevent/ares.pyx gevent/*.pxd
$(CYTHON) -o gevent.ares.c gevent/ares.pyx
mv gevent.ares.* gevent/
gevent/gevent._semaphore.c: gevent/_semaphore.pyx gevent/_semaphore.pxd
# For PyPy, we need to have _semaphore named as a .pyx file so it doesn't
# get loaded in preference to the .so. But we want to keep the definitions
# separate in a .pxd file for ease of reading, and that only works
# with .py files.
cp gevent/_semaphore.pyx gevent/_semaphore.py
$(CYTHON) -o gevent._semaphore.c gevent/_semaphore.py
mv gevent._semaphore.* gevent/
rm gevent/_semaphore.py
gevent/gevent._util.c: gevent/_util.pyx
$(CYTHON) -o gevent._util.c gevent/_util.pyx
mv gevent._util.* gevent/
clean:
rm -f gevent.core.c gevent.core.h core.pyx gevent/gevent.core.c gevent/gevent.core.h gevent/core.pyx
rm -f gevent.corecext.c gevent.corecext.h gevent/gevent.corecext.c gevent/gevent.corecext.h
rm -f gevent.ares.c gevent.ares.h gevent/gevent.ares.c gevent/gevent.ares.h
rm -f gevent._semaphore.c gevent._semaphore.h gevent/gevent._semaphore.c gevent/gevent._semaphore.h
rm -f gevent._util.c gevent._util.h gevent/gevent._util.c gevent/gevent._util.h
doc:
cd doc && PYTHONPATH=.. make html
whitespace:
! find . -not -path "./.tox/*" -not -path "*/__pycache__/*" -not -path "*.so" -not -path "*.pyc" -not -path "./.git/*" -not -path "./build/*" -not -path "./libev/*" -not -path "./gevent/libev/*" -not -path "./gevent.egg-info/*" -not -path "./dist/*" -not -path "./.DS_Store" -not -path "./c-ares/*" -not -path "./gevent/gevent.*.[ch]" -not -path "./gevent/core.pyx" -not -path "./doc/_build/*" -not -path "./doc/mytheme/static/*" -type f | xargs egrep -l " $$"
pep8:
${PYTHON} `which pep8` .
pyflakes:
${PYTHON} util/pyflakes.py
lint: whitespace pyflakes pep8
travistest:
which ${PYTHON}
${PYTHON} --version
${PYTHON} -c 'import greenlet; print(greenlet, greenlet.__version__)'
${PYTHON} setup.py install
make bench
cd greentest && GEVENT_RESOLVER=thread ${PYTHON} testrunner.py --config ../known_failures.py
cd greentest && GEVENT_RESOLVER=ares GEVENTARES_SERVERS=8.8.8.8 ${PYTHON} testrunner.py --config ../known_failures.py --ignore tests_that_dont_use_resolver.txt
cd greentest && GEVENT_FILE=thread ${PYTHON} testrunner.py --config ../known_failures.py `grep -l subprocess test_*.py`
toxtest:
cd greentest && GEVENT_RESOLVER=thread python testrunner.py --config ../known_failures.py
fulltoxtest:
cd greentest && GEVENT_RESOLVER=thread python testrunner.py --config ../known_failures.py
cd greentest && GEVENT_RESOLVER=ares GEVENTARES_SERVERS=8.8.8.8 python testrunner.py --config ../known_failures.py --ignore tests_that_dont_use_resolver.txt
cd greentest && GEVENT_FILE=thread python testrunner.py --config ../known_failures.py `grep -l subprocess test_*.py`
leaktest:
GEVENTSETUP_EV_VERIFY=3 GEVENTTEST_LEAKCHECK=1 make travistest
bench:
${PYTHON} greentest/bench_sendall.py
travis_pypy:
which ${PYTHON}
${PYTHON} --version
${PYTHON} setup.py install
make bench
cd greentest && ${PYTHON} testrunner.py --config ../known_failures.py
travis_cpython:
pip install cython greenlet
make travistest
travis_test_linters:
make lint
make leaktest
.PHONY: clean all doc pep8 whitespace pyflakes lint travistest travis
......@@ -7,7 +7,10 @@
Unreleased
==========
- (Experimental) Enable the c-ares resolver extension for PyPy.
- Enable the c-ares resolver extension for PyPy.
- On some versions of PyPy on some platforms (notably 2.6.0 on 64-bit
Linux), enabling ``gevent.monkey.patch_builtins`` could cause PyPy
to crash. Reported in :issue:`618` by Jay Oster.
1.1b1 (Jul 17, 2015)
====================
......
......@@ -143,18 +143,20 @@ class Semaphore(object):
switch = getcurrent().switch
self.rawlink(switch)
try:
timer = Timeout.start_new(timeout)
# As a tiny efficiency optimization, avoid allocating a timer
# if not needed.
timer = Timeout.start_new(timeout) if timeout is not None else None
try:
try:
result = get_hub().switch()
assert result is self, 'Invalid switch into Semaphore.acquire(): %r' % (result, )
except Timeout:
ex = sys.exc_info()[1]
except Timeout as ex:
if ex is timer:
return False
raise
finally:
timer.cancel()
if timer is not None:
timer.cancel()
finally:
self.unlink(switch)
self.counter -= 1
......
......@@ -38,13 +38,7 @@ def __import__(*args, **kwargs):
args = args[1:]
imp.acquire_lock()
try:
try:
_g_import_lock.acquire()
except RuntimeError:
# we've seen this under PyPy, a recursion error
# importing 'platform'
return _import(*args, **kwargs)
_g_import_lock.acquire()
try:
result = _import(*args, **kwargs)
finally:
......
......@@ -20,6 +20,22 @@ __all__ = ['Timeout',
'with_timeout']
class _FakeTimer(object):
# An object that mimics the API of get_hub().loop.timer, but
# without allocating any native resources. This is useful for timeouts
# that will never expire
pending = False
active = False
def start(self, *args, **kwargs):
raise AssertionError("non-expiring timer cannot be started")
def stop(self):
return
_FakeTimer = _FakeTimer()
class Timeout(BaseException):
"""
Raise *exception* in the current greenlet after given time period::
......@@ -86,19 +102,33 @@ class Timeout(BaseException):
``Timeout()``), then the timeout will never expire and never raise
*exception*. This is convenient for creating functions which take
an optional timeout parameter of their own.
.. versionchanged:: 1.1b2
If *seconds* is not given or is ``None``, no longer allocate a libev
timer that will never be started.
"""
def __init__(self, seconds=None, exception=None, ref=True, priority=-1):
self.seconds = seconds
self.exception = exception
self.timer = get_hub().loop.timer(seconds or 0.0, ref=ref, priority=priority)
if seconds is None:
# Avoid going through the timer codepath if no timeout is
# desired; this avoids some CFFI interactions on PyPy that can lead to a
# RuntimeError if this implementation is used during an `import` statement. See
# https://bitbucket.org/pypy/pypy/issues/2089/crash-in-pypy-260-linux64-with-gevent-11b1
# and https://github.com/gevent/gevent/issues/618.
# Plus, in general, it should be more efficient
self.timer = _FakeTimer
else:
self.timer = get_hub().loop.timer(seconds or 0.0, ref=ref, priority=priority)
def start(self):
"""Schedule the timeout."""
assert not self.pending, '%r is already started; to restart it, cancel it first' % self
if self.seconds is None: # "fake" timeout (never expires)
pass
elif self.exception is None or self.exception is False or isinstance(self.exception, string_types):
return
if self.exception is None or self.exception is False or isinstance(self.exception, string_types):
# timeout that raises self
self.timer.start(getcurrent().throw, self)
else: # regular timeout with user-provided exception
......
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