aboutsummaryrefslogtreecommitdiffstats
path: root/python/gevent/lock.py
diff options
context:
space:
mode:
authorJames Taylor <user234683@users.noreply.github.com>2018-07-12 23:40:30 -0700
committerJames Taylor <user234683@users.noreply.github.com>2018-07-12 23:41:07 -0700
commitc3b9f8c4582882cd1f768b0727eca75475bb4f94 (patch)
tree5b4a1c693fd5b7416f1d5a75862e633502e77ca7 /python/gevent/lock.py
parentfe9fe8257740529f5880693992e4eeca35c7ea3e (diff)
downloadyt-local-c3b9f8c4582882cd1f768b0727eca75475bb4f94.tar.lz
yt-local-c3b9f8c4582882cd1f768b0727eca75475bb4f94.tar.xz
yt-local-c3b9f8c4582882cd1f768b0727eca75475bb4f94.zip
track embedded python distribution
Diffstat (limited to 'python/gevent/lock.py')
-rw-r--r--python/gevent/lock.py260
1 files changed, 260 insertions, 0 deletions
diff --git a/python/gevent/lock.py b/python/gevent/lock.py
new file mode 100644
index 0000000..506d8c5
--- /dev/null
+++ b/python/gevent/lock.py
@@ -0,0 +1,260 @@
+# Copyright (c) 2009-2012 Denis Bilenko. See LICENSE for details.
+"""Locking primitives"""
+from __future__ import absolute_import
+
+from gevent.hub import getcurrent
+from gevent._compat import PYPY
+from gevent._semaphore import Semaphore, BoundedSemaphore # pylint:disable=no-name-in-module,import-error
+
+
+__all__ = [
+ 'Semaphore',
+ 'DummySemaphore',
+ 'BoundedSemaphore',
+ 'RLock',
+]
+
+# On PyPy, we don't compile the Semaphore class with Cython. Under
+# Cython, each individual method holds the GIL for its entire
+# 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
+# allow switching threads). Simulate that here through the use of a manual
+# lock. (We use a separate lock for each semaphore to allow sys.settrace functions
+# to use locks *other* than the one being traced.)
+if PYPY:
+ # TODO: Need to use monkey.get_original?
+ try:
+ from _thread import allocate_lock as _allocate_lock # pylint:disable=import-error,useless-suppression
+ from _thread import get_ident as _get_ident # pylint:disable=import-error,useless-suppression
+ except ImportError:
+ # Python 2
+ from thread import allocate_lock as _allocate_lock # pylint:disable=import-error,useless-suppression
+ from thread import get_ident as _get_ident # pylint:disable=import-error,useless-suppression
+ _sem_lock = _allocate_lock()
+
+ def untraceable(f):
+ # Don't allow re-entry to these functions in a single thread, as can
+ # happen if a sys.settrace is used
+ def wrapper(self):
+ me = _get_ident()
+ try:
+ count = self._locking[me]
+ except KeyError:
+ count = self._locking[me] = 1
+ else:
+ count = self._locking[me] = count + 1
+ if count:
+ return
+
+ try:
+ return f(self)
+ finally:
+ count = count - 1
+ if not count:
+ del self._locking[me]
+ else:
+ self._locking[me] = count
+ return wrapper
+
+ class _OwnedLock(object):
+
+ def __init__(self):
+ self._owner = None
+ self._block = _allocate_lock()
+ self._locking = {}
+ self._count = 0
+
+ @untraceable
+ def acquire(self):
+ me = _get_ident()
+ if self._owner == me:
+ self._count += 1
+ return
+
+ self._owner = me
+ self._block.acquire()
+ self._count = 1
+
+ @untraceable
+ def release(self):
+ self._count = count = self._count - 1
+ if not count:
+ self._block.release()
+ self._owner = None
+
+ # acquire, wait, and release all acquire the lock on entry and release it
+ # on exit. acquire and wait can call _do_wait, which must release it on entry
+ # and re-acquire it for them on exit.
+ class _around(object):
+ __slots__ = ('before', 'after')
+
+ def __init__(self, before, after):
+ self.before = before
+ self.after = after
+
+ def __enter__(self):
+ self.before()
+
+ def __exit__(self, t, v, tb):
+ self.after()
+
+ def _decorate(func, cmname):
+ # functools.wrap?
+ def wrapped(self, *args, **kwargs):
+ with getattr(self, cmname):
+ return func(self, *args, **kwargs)
+ return wrapped
+
+ Semaphore._py3k_acquire = Semaphore.acquire = _decorate(Semaphore.acquire, '_lock_locked')
+ Semaphore.release = _decorate(Semaphore.release, '_lock_locked')
+ Semaphore.wait = _decorate(Semaphore.wait, '_lock_locked')
+ Semaphore._do_wait = _decorate(Semaphore._do_wait, '_lock_unlocked')
+
+ _Sem_init = Semaphore.__init__
+
+ def __init__(self, *args, **kwargs):
+ l = self._lock_lock = _OwnedLock()
+ self._lock_locked = _around(l.acquire, l.release)
+ self._lock_unlocked = _around(l.release, l.acquire)
+
+ _Sem_init(self, *args, **kwargs)
+
+ Semaphore.__init__ = __init__
+
+ del _decorate
+ del untraceable
+
+
+class DummySemaphore(object):
+ """
+ DummySemaphore(value=None) -> DummySemaphore
+
+ A Semaphore initialized with "infinite" initial value. None of its
+ methods ever block.
+
+ This can be used to parameterize on whether or not to actually
+ guard access to a potentially limited resource. If the resource is
+ actually limited, such as a fixed-size thread pool, use a real
+ :class:`Semaphore`, but if the resource is unbounded, use an
+ instance of this class. In that way none of the supporting code
+ needs to change.
+
+ Similarly, it can be used to parameterize on whether or not to
+ enforce mutual exclusion to some underlying object. If the
+ underlying object is known to be thread-safe itself mutual
+ exclusion is not needed and a ``DummySemaphore`` can be used, but
+ if that's not true, use a real ``Semaphore``.
+ """
+
+ # Internally this is used for exactly the purpose described in the
+ # documentation. gevent.pool.Pool uses it instead of a Semaphore
+ # when the pool size is unlimited, and
+ # gevent.fileobject.FileObjectThread takes a parameter that
+ # determines whether it should lock around IO to the underlying
+ # file object.
+
+ def __init__(self, value=None):
+ """
+ .. versionchanged:: 1.1rc3
+ Accept and ignore a *value* argument for compatibility with Semaphore.
+ """
+ pass
+
+ def __str__(self):
+ return '<%s>' % self.__class__.__name__
+
+ def locked(self):
+ """A DummySemaphore is never locked so this always returns False."""
+ return False
+
+ def release(self):
+ """Releasing a dummy semaphore does nothing."""
+ pass
+
+ def rawlink(self, callback):
+ # XXX should still work and notify?
+ pass
+
+ def unlink(self, callback):
+ pass
+
+ def wait(self, timeout=None):
+ """Waiting for a DummySemaphore returns immediately."""
+ pass
+
+ def acquire(self, blocking=True, timeout=None):
+ """
+ A DummySemaphore can always be acquired immediately so this always
+ returns True and ignores its arguments.
+
+ .. versionchanged:: 1.1a1
+ Always return *true*.
+ """
+ # pylint:disable=unused-argument
+ return True
+
+ def __enter__(self):
+ pass
+
+ def __exit__(self, typ, val, tb):
+ pass
+
+
+class RLock(object):
+
+ def __init__(self):
+ self._block = Semaphore(1)
+ self._owner = None
+ self._count = 0
+
+ def __repr__(self):
+ return "<%s at 0x%x _block=%s _count=%r _owner=%r)>" % (
+ self.__class__.__name__,
+ id(self),
+ self._block,
+ self._count,
+ self._owner)
+
+ def acquire(self, blocking=1):
+ me = getcurrent()
+ if self._owner is me:
+ self._count = self._count + 1
+ return 1
+ rc = self._block.acquire(blocking)
+ if rc:
+ self._owner = me
+ self._count = 1
+ return rc
+
+ def __enter__(self):
+ return self.acquire()
+
+ def release(self):
+ if self._owner is not getcurrent():
+ raise RuntimeError("cannot release un-aquired lock")
+ self._count = count = self._count - 1
+ if not count:
+ self._owner = None
+ self._block.release()
+
+ def __exit__(self, typ, value, tb):
+ self.release()
+
+ # Internal methods used by condition variables
+
+ def _acquire_restore(self, count_owner):
+ count, owner = count_owner
+ self._block.acquire()
+ self._count = count
+ self._owner = owner
+
+ def _release_save(self):
+ count = self._count
+ self._count = 0
+ owner = self._owner
+ self._owner = None
+ self._block.release()
+ return (count, owner)
+
+ def _is_owned(self):
+ return self._owner is getcurrent()