diff options
Diffstat (limited to 'python/gevent/_semaphore.py')
-rw-r--r-- | python/gevent/_semaphore.py | 48 |
1 files changed, 41 insertions, 7 deletions
diff --git a/python/gevent/_semaphore.py b/python/gevent/_semaphore.py index c6e1162..d7c67ce 100644 --- a/python/gevent/_semaphore.py +++ b/python/gevent/_semaphore.py @@ -1,10 +1,22 @@ +# cython: auto_pickle=False,embedsignature=True,always_allow_keywords=False +from __future__ import print_function, absolute_import, division import sys -from gevent.hub import get_hub, getcurrent + from gevent.timeout import Timeout -__all__ = ['Semaphore', 'BoundedSemaphore'] +__all__ = [ + 'Semaphore', + 'BoundedSemaphore', +] +# In Cython, we define these as 'cdef [inline]' functions. The +# compilation unit cannot have a direct assignment to them (import +# is assignment) without generating a 'lvalue is not valid target' +# error. +locals()['getcurrent'] = __import__('greenlet').getcurrent +locals()['greenlet_init'] = lambda: None +locals()['get_hub'] = __import__('gevent').get_hub class Semaphore(object): """ @@ -75,7 +87,8 @@ class Semaphore(object): # NOTE: Passing the bound method will cause a memory leak on PyPy # with Cython <= 0.23.3. You must use >= 0.23.4. # See https://bitbucket.org/pypy/pypy/issues/2149/memory-leak-for-python-subclass-of-cpyext#comment-22371546 - self._notifier = get_hub().loop.run_callback(self._notify_links) + hub = get_hub() # pylint:disable=undefined-variable + self._notifier = hub.loop.run_callback(self._notify_links) def _notify_links(self): # Subclasses CANNOT override. This is a cdef method. @@ -100,7 +113,7 @@ class Semaphore(object): try: link(self) # Must use Cython >= 0.23.4 on PyPy else this leaks memory except: # pylint:disable=bare-except - getcurrent().handle_error((link, self), *sys.exc_info()) + getcurrent().handle_error((link, self), *sys.exc_info()) # pylint:disable=undefined-variable if self._dirty: # We mutated self._links so we need to start over break @@ -157,13 +170,13 @@ class Semaphore(object): elapses, return the exception. Otherwise, return None. Raises timeout if a different timer expires. """ - switch = getcurrent().switch + switch = getcurrent().switch # pylint:disable=undefined-variable self.rawlink(switch) try: timer = Timeout._start_new_or_dummy(timeout) try: try: - result = get_hub().switch() + result = get_hub().switch() # pylint:disable=undefined-variable assert result is self, 'Invalid switch into Semaphore.wait/acquire(): %r' % (result, ) except Timeout as ex: if ex is not timer: @@ -266,4 +279,25 @@ class BoundedSemaphore(Semaphore): def release(self): if self.counter >= self._initial_value: raise self._OVER_RELEASE_ERROR("Semaphore released too many times") - return Semaphore.release(self) + Semaphore.release(self) + + +def _init(): + greenlet_init() # pylint:disable=undefined-variable + +_init() + +# By building the semaphore with Cython under PyPy, we get +# atomic operations (specifically, exiting/releasing), at the +# cost of some speed (one trivial semaphore micro-benchmark put the pure-python version +# at around 1s and the compiled version at around 4s). Some clever subclassing +# and having only the bare minimum be in cython might help reduce that penalty. +# NOTE: You must use version 0.23.4 or later to avoid a memory leak. +# https://mail.python.org/pipermail/cython-devel/2015-October/004571.html +# However, that's all for naught on up to and including PyPy 4.0.1 which +# have some serious crashing bugs with GC interacting with cython. +# It hasn't been tested since then, and PURE_PYTHON is assumed to be true +# for PyPy in all cases anyway, so this does nothing. + +from gevent._util import import_c_accel +import_c_accel(globals(), 'gevent.__semaphore') |