Commit 41319891 authored by Jason Madden's avatar Jason Madden

Documentation and comment tweaks. [skip ci]

parent 4fcd6f8f
...@@ -37,7 +37,8 @@ PyPy Notes ...@@ -37,7 +37,8 @@ PyPy Notes
PyPy has been tested on OS X and 64-bit Linux from version 2.6.1 PyPy has been tested on OS X and 64-bit Linux from version 2.6.1
through 4.0.0 and 4.0.1. through 4.0.0 and 4.0.1.
.. note:: PyPy is not supported on Windows. .. note:: PyPy is not supported on Windows. (gevent's CFFI backend is not
available on Windows.)
- Version 2.6.1 or above is required for proper signal handling. Prior - Version 2.6.1 or above is required for proper signal handling. Prior
to 2.6.1 and its inclusion of `cffi 1.3.0`_, signals could be to 2.6.1 and its inclusion of `cffi 1.3.0`_, signals could be
...@@ -50,19 +51,20 @@ through 4.0.0 and 4.0.1. ...@@ -50,19 +51,20 @@ through 4.0.0 and 4.0.1.
of PyPy. The benchmarks distributed with gevent typically perform as of PyPy. The benchmarks distributed with gevent typically perform as
well or better on PyPy than on CPython at least on some platforms. well or better on PyPy than on CPython at least on some platforms.
Things that are known or expected to be (relatively) slower under Things that are known or expected to be (relatively) slower under
PyPy include the :mod:`c-ares resolver <gevent.resolver_ares>`. PyPy include the :mod:`c-ares resolver <gevent.resolver_ares>` and
Whether or not these matter will depend on the workload of each :class:`~gevent.lock.Semaphore`. Whether or not these matter will
application. depend on the workload of each application.
.. caution:: The ``c-ares`` resolver is not recommended for use under .. caution:: The ``c-ares`` resolver is considered highly experimental
PyPy. Released versions of PyPy through at least 4.0.1 under PyPy and is not recommended for production use.
have `a bug`_ that can cause a memory leak when Released versions of PyPy through at least 4.0.1 have `a
subclassing objects that are implemented in Cython, as is bug`_ that can cause a memory leak when subclassing
the c-ares resolver. things mentioned above. The objects that are implemented in Cython, as is the c-ares
``c-ares`` package has not been audited for this issue. resolver. In addition, thanks to reports like
In addition, thanks to reports like :issue:`704`, we know :issue:`704`, we know that the PyPy garbage collector can
that the PyPy garbage collector can interact badly with interact badly with Cython-compiled code, leading to
Cython-compiled code, leading to crashes. crashes. While the intended use of the ares resolver has
been loosely audited for these issues, no guarantees are made.
.. note:: PyPy 4.0.x on Linux is known to *rarely* (once per 24 hours) .. note:: PyPy 4.0.x on Linux is known to *rarely* (once per 24 hours)
encounter crashes when running heavily loaded, heavily encounter crashes when running heavily loaded, heavily
......
...@@ -17,7 +17,13 @@ class Semaphore(object): ...@@ -17,7 +17,13 @@ class Semaphore(object):
If not given, ``value`` defaults to 1. If not given, ``value`` defaults to 1.
This Semaphore's ``__exit__`` method does not call the trace function. The semaphore is a context manager and can be used in ``with`` statements.
This Semaphore's ``__exit__`` method does not call the trace function
on CPython, but does under PyPy.
.. seealso:: :class:`BoundedSemaphore` for a safer version that prevents
some classes of bugs.
""" """
def __init__(self, value=1): def __init__(self, value=1):
...@@ -53,6 +59,9 @@ class Semaphore(object): ...@@ -53,6 +59,9 @@ class Semaphore(object):
return self.counter <= 0 return self.counter <= 0
def release(self): def release(self):
"""
Release the semaphore, notifying any waiters if needed.
"""
self.counter += 1 self.counter += 1
self._start_notify() self._start_notify()
return self.counter return self.counter
...@@ -112,6 +121,9 @@ class Semaphore(object): ...@@ -112,6 +121,9 @@ class Semaphore(object):
*callback* will be called in the :class:`Hub <gevent.hub.Hub>`, so it must not use blocking gevent API. *callback* will be called in the :class:`Hub <gevent.hub.Hub>`, so it must not use blocking gevent API.
*callback* will be passed one argument: this instance. *callback* will be passed one argument: this instance.
This method is normally called automatically by :meth:`acquire` and :meth:`wait`; most code
will not need to use it.
""" """
if not callable(callback): if not callable(callback):
raise TypeError('Expected callable:', callback) raise TypeError('Expected callable:', callback)
...@@ -125,7 +137,10 @@ class Semaphore(object): ...@@ -125,7 +137,10 @@ class Semaphore(object):
""" """
unlink(callback) -> None unlink(callback) -> None
Remove the callback set by :meth:`rawlink` Remove the callback set by :meth:`rawlink`.
This method is normally called automatically by :meth:`acquire` and :meth:`wait`; most
code will not need to use it.
""" """
try: try:
self._links.remove(callback) self._links.remove(callback)
...@@ -169,7 +184,7 @@ class Semaphore(object): ...@@ -169,7 +184,7 @@ class Semaphore(object):
Wait until it is possible to acquire this semaphore, or until the optional Wait until it is possible to acquire this semaphore, or until the optional
*timeout* elapses. *timeout* elapses.
.. warning:: If this semaphore was initialized with a size of 0, .. caution:: If this semaphore was initialized with a size of 0,
this method will block forever if no timeout is given. this method will block forever if no timeout is given.
:keyword float timeout: If given, specifies the maximum amount of seconds :keyword float timeout: If given, specifies the maximum amount of seconds
...@@ -189,7 +204,7 @@ class Semaphore(object): ...@@ -189,7 +204,7 @@ class Semaphore(object):
Acquire the semaphore. Acquire the semaphore.
.. warning:: If this semaphore was initialized with a size of 0, .. caution:: If this semaphore was initialized with a size of 0,
this method will block forever (unless a timeout is given or blocking is this method will block forever (unless a timeout is given or blocking is
set to false). set to false).
...@@ -233,6 +248,8 @@ class Semaphore(object): ...@@ -233,6 +248,8 @@ class Semaphore(object):
class BoundedSemaphore(Semaphore): class BoundedSemaphore(Semaphore):
""" """
BoundedSemaphore(value=1) -> BoundedSemaphore
A bounded semaphore checks to make sure its current value doesn't A bounded semaphore checks to make sure its current value doesn't
exceed its initial value. If it does, :class:`ValueError` is exceed its initial value. If it does, :class:`ValueError` is
raised. In most situations semaphores are used to guard resources raised. In most situations semaphores are used to guard resources
...@@ -242,11 +259,12 @@ class BoundedSemaphore(Semaphore): ...@@ -242,11 +259,12 @@ class BoundedSemaphore(Semaphore):
If not given, *value* defaults to 1. If not given, *value* defaults to 1.
""" """
#: For monkey-patching, allow changing the class of error we raise
_OVER_RELEASE_ERROR = ValueError _OVER_RELEASE_ERROR = ValueError
def __init__(self, value=1): def __init__(self, *args, **kwargs):
Semaphore.__init__(self, value) Semaphore.__init__(self, *args, **kwargs)
self._initial_value = value self._initial_value = self.counter
def release(self): def release(self):
if self.counter >= self._initial_value: if self.counter >= self._initial_value:
......
...@@ -161,7 +161,7 @@ def wait_read(fileno, timeout=None, timeout_exc=_NONE): ...@@ -161,7 +161,7 @@ def wait_read(fileno, timeout=None, timeout_exc=_NONE):
For the meaning of the other parameters and possible exceptions, For the meaning of the other parameters and possible exceptions,
see :func:`wait`. see :func:`wait`.
.. seealso: :func:`cancel_wait` .. seealso:: :func:`cancel_wait`
""" """
io = get_hub().loop.io(fileno, 1) io = get_hub().loop.io(fileno, 1)
return wait(io, timeout, timeout_exc) return wait(io, timeout, timeout_exc)
...@@ -177,7 +177,7 @@ def wait_write(fileno, timeout=None, timeout_exc=_NONE, event=_NONE): ...@@ -177,7 +177,7 @@ def wait_write(fileno, timeout=None, timeout_exc=_NONE, event=_NONE):
:keyword event: Ignored. Applications should not pass this parameter. :keyword event: Ignored. Applications should not pass this parameter.
In the future, it may become an error. In the future, it may become an error.
.. seealso: :func:`cancel_wait` .. seealso:: :func:`cancel_wait`
""" """
io = get_hub().loop.io(fileno, 2) io = get_hub().loop.io(fileno, 2)
return wait(io, timeout, timeout_exc) return wait(io, timeout, timeout_exc)
...@@ -194,7 +194,7 @@ def wait_readwrite(fileno, timeout=None, timeout_exc=_NONE, event=_NONE): ...@@ -194,7 +194,7 @@ def wait_readwrite(fileno, timeout=None, timeout_exc=_NONE, event=_NONE):
:keyword event: Ignored. Applications should not pass this parameter. :keyword event: Ignored. Applications should not pass this parameter.
In the future, it may become an error. In the future, it may become an error.
.. seealso: :func:`cancel_wait` .. seealso:: :func:`cancel_wait`
""" """
io = get_hub().loop.io(fileno, 3) io = get_hub().loop.io(fileno, 3)
return wait(io, timeout, timeout_exc) return wait(io, timeout, timeout_exc)
......
...@@ -13,8 +13,8 @@ __all__ = ['Semaphore', 'DummySemaphore', 'BoundedSemaphore', 'RLock'] ...@@ -13,8 +13,8 @@ __all__ = ['Semaphore', 'DummySemaphore', 'BoundedSemaphore', 'RLock']
# duration, ensuring that no other thread can interrupt us in an # duration, ensuring that no other thread can interrupt us in an
# unsafe state (only when we _do_wait do we call back into Python and # unsafe state (only when we _do_wait do we call back into Python and
# allow switching threads). Simulate that here through the use of a manual # allow switching threads). Simulate that here through the use of a manual
# lock. (In theory we could use a lock per Semaphore and thus potentially # lock. (We use a separate lock for each semaphore to allow sys.settrace functions
# scale better than the GIL, but it probably doesn't matter.) # to use locks *other* than the one being traced.)
if PYPY: if PYPY:
# TODO: Need to use monkey.get_original? # TODO: Need to use monkey.get_original?
from thread import allocate_lock as _allocate_lock from thread import allocate_lock as _allocate_lock
...@@ -115,6 +115,8 @@ if PYPY: ...@@ -115,6 +115,8 @@ if PYPY:
class DummySemaphore(object): class DummySemaphore(object):
""" """
DummySemaphore(value=None) -> DummySemaphore
A Semaphore initialized with "infinite" initial value. None of its A Semaphore initialized with "infinite" initial value. None of its
methods ever block. methods ever block.
...@@ -139,6 +141,13 @@ class DummySemaphore(object): ...@@ -139,6 +141,13 @@ class DummySemaphore(object):
# determines whether it should lock around IO to the underlying # determines whether it should lock around IO to the underlying
# file object. # file object.
def __init__(self, value=None):
"""
.. versionchanged:: 1.1rc3
Accept and ignore a *value* argument for compatibility with Semaphore.
"""
pass
def __str__(self): def __str__(self):
return '<%s>' % self.__class__.__name__ return '<%s>' % self.__class__.__name__
...@@ -147,6 +156,7 @@ class DummySemaphore(object): ...@@ -147,6 +156,7 @@ class DummySemaphore(object):
return False return False
def release(self): def release(self):
"""Releasing a dummy semaphore does nothing."""
pass pass
def rawlink(self, callback): def rawlink(self, callback):
...@@ -157,6 +167,7 @@ class DummySemaphore(object): ...@@ -157,6 +167,7 @@ class DummySemaphore(object):
pass pass
def wait(self, timeout=None): def wait(self, timeout=None):
"""Waiting for a DummySemaphore returns immediately."""
pass pass
def acquire(self, blocking=True, timeout=None): def acquire(self, blocking=True, timeout=None):
......
...@@ -10,6 +10,7 @@ from os.path import join, abspath, basename, dirname ...@@ -10,6 +10,7 @@ from os.path import join, abspath, basename, dirname
from subprocess import check_call from subprocess import check_call
from glob import glob from glob import glob
PYPY = hasattr(sys, 'pypy_version_info') PYPY = hasattr(sys, 'pypy_version_info')
WIN = sys.platform.startswith('win') WIN = sys.platform.startswith('win')
CFFI_WIN_BUILD_ANYWAY = os.environ.get("PYPY_WIN_BUILD_ANYWAY") CFFI_WIN_BUILD_ANYWAY = os.environ.get("PYPY_WIN_BUILD_ANYWAY")
...@@ -31,6 +32,8 @@ if WIN: ...@@ -31,6 +32,8 @@ if WIN:
# Make sure the env vars that make.cmd needs are set # Make sure the env vars that make.cmd needs are set
if not os.environ.get('PYTHON_EXE'): if not os.environ.get('PYTHON_EXE'):
os.environ['PYTHON_EXE'] = 'pypy' if PYPY else 'python' os.environ['PYTHON_EXE'] = 'pypy' if PYPY else 'python'
if not os.environ.get('PYEXE'):
os.environ['PYEXE'] = os.environ['PYTHON_EXE']
import distutils import distutils
......
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