[pypy-commit] pypy py3k: Add _thread.RLock

amauryfa noreply at buildbot.pypy.org
Sat Jan 14 21:48:30 CET 2012


Author: Amaury Forgeot d'Arc <amauryfa at gmail.com>
Branch: py3k
Changeset: r51321:ae023502cd1a
Date: 2011-12-26 17:19 +0100
http://bitbucket.org/pypy/pypy/changeset/ae023502cd1a/

Log:	Add _thread.RLock

diff --git a/pypy/module/thread/__init__.py b/pypy/module/thread/__init__.py
--- a/pypy/module/thread/__init__.py
+++ b/pypy/module/thread/__init__.py
@@ -20,6 +20,7 @@
         'allocate_lock':          'os_lock.allocate_lock',
         'allocate':               'os_lock.allocate_lock',  # obsolete synonym
         'LockType':               'os_lock.Lock',
+        'RLock':                  'os_lock.W_RLock',
         '_local':                 'os_local.Local',
         'TIMEOUT_MAX':            'space.wrap(float(os_lock.TIMEOUT_MAX) / 1000000.0)',
         'error':                  'space.fromcache(error.Cache).w_error',
diff --git a/pypy/module/thread/os_lock.py b/pypy/module/thread/os_lock.py
--- a/pypy/module/thread/os_lock.py
+++ b/pypy/module/thread/os_lock.py
@@ -6,9 +6,9 @@
 from pypy.module.thread.error import wrap_thread_error
 from pypy.interpreter.baseobjspace import Wrappable
 from pypy.interpreter.gateway import interp2app, unwrap_spec
-from pypy.interpreter.typedef import TypeDef
+from pypy.interpreter.typedef import TypeDef, make_weakref_descr
 from pypy.interpreter.error import OperationError
-from pypy.rlib.rarithmetic import r_longlong
+from pypy.rlib.rarithmetic import r_longlong, r_uint, ovfcheck
 
 # Force the declaration of the type 'thread.LockType' for RPython
 #import pypy.module.thread.rpython.exttable
@@ -100,14 +100,8 @@
     def __exit__(self, *args):
         self.descr_lock_release(self.space)
 
-descr_acquire = interp2app(Lock.descr_lock_acquire)
-descr_release = interp2app(Lock.descr_lock_release)
-descr_locked  = interp2app(Lock.descr_lock_locked)
-descr__enter__ = interp2app(Lock.descr__enter__)
-descr__exit__ = interp2app(Lock.descr__exit__)
-
-
-Lock.typedef = TypeDef("thread.lock",
+Lock.typedef = TypeDef(
+    "_thread.lock",
     __doc__ = """\
 A lock object is a synchronization primitive.  To create a lock,
 call the thread.allocate_lock() function.  Methods are:
@@ -119,15 +113,15 @@
 A lock is not owned by the thread that locked it; another thread may
 unlock it.  A thread attempting to lock a lock that it has already locked
 will block until another thread unlocks it.  Deadlocks may ensue.""",
-    acquire = descr_acquire,
-    release = descr_release,
-    locked  = descr_locked,
-    __enter__ = descr__enter__,
-    __exit__ = descr__exit__,
+    acquire = interp2app(Lock.descr_lock_acquire),
+    release = interp2app(Lock.descr_lock_release),
+    locked  = interp2app(Lock.descr_lock_locked),
+    __enter__ = interp2app(Lock.descr__enter__),
+    __exit__ = interp2app(Lock.descr__exit__),
     # Obsolete synonyms
-    acquire_lock = descr_acquire,
-    release_lock = descr_release,
-    locked_lock  = descr_locked,
+    acquire_lock = interp2app(Lock.descr_lock_acquire),
+    release_lock = interp2app(Lock.descr_lock_release),
+    locked_lock  = interp2app(Lock.descr_lock_locked),
     )
 
 
@@ -135,3 +129,123 @@
     """Create a new lock object.  (allocate() is an obsolete synonym.)
 See LockType.__doc__ for information about locks."""
     return space.wrap(Lock(space))
+
+
+class W_RLock(Wrappable):
+    def __init__(self, space):
+        self.rlock_count = 0
+        self.rlock_owner = 0
+        try:
+            self.lock = thread.allocate_lock()
+        except thread.error:
+            raise wrap_thread_error(space, "cannot allocate lock")
+
+    def descr__new__(space, w_subtype):
+        self = space.allocate_instance(W_RLock, w_subtype)
+        W_RLock.__init__(self, space)
+        return space.wrap(self)
+
+    def descr__repr__(self):
+        typename = space.type(self).getname(space)
+        return space.wrap("<%s owner=%d count=%d>" % (
+                typename, self.rlock_owner, self.rlock_count))
+
+    @unwrap_spec(blocking=bool)
+    def acquire_w(self, space, blocking=True):
+        """Lock the lock.  `blocking` indicates whether we should wait
+        for the lock to be available or not.  If `blocking` is False
+        and another thread holds the lock, the method will return False
+        immediately.  If `blocking` is True and another thread holds
+        the lock, the method will wait for the lock to be released,
+        take it and then return True.
+        (note: the blocking operation is not interruptible.)
+        
+        In all other cases, the method will return True immediately.
+        Precisely, if the current thread already holds the lock, its
+        internal counter is simply incremented. If nobody holds the lock,
+        the lock is taken and its internal counter initialized to 1."""
+        tid = thread.get_ident()
+        if self.rlock_count > 0 and tid == self.rlock_owner:
+            try:
+                self.rlock_count = ovfcheck(self.rlock_count + 1)
+            except OverflowError:
+                raise OperationError(space.w_OverflowError, space.wrap(
+                        'internal lock count overflowed'))
+            return space.w_True
+
+        r = True
+        if self.rlock_count > 0 or not self.lock.acquire(False):
+            if not blocking:
+                return space.w_False
+            r = self.lock.acquire(True)
+        if r:
+            assert self.rlock_count == 0
+            self.rlock_owner = tid
+            self.rlock_count = 1
+
+        return space.wrap(r)
+            
+
+    def release_w(self, space):
+        """Release the lock, allowing another thread that is blocked waiting for
+        the lock to acquire the lock.  The lock must be in the locked state,
+        and must be locked by the same thread that unlocks it; otherwise a
+        `RuntimeError` is raised.
+        
+        Do note that if the lock was acquire()d several times in a row by the
+        current thread, release() needs to be called as many times for the lock
+        to be available for other threads."""
+        tid = thread.get_ident()
+        if self.rlock_count == 0 or self.rlock_owner != tid:
+            raise OperationError(space.w_RuntimeError, space.wrap(
+                    "cannot release un-acquired lock"))
+        self.rlock_count -= 1
+        if self.rlock_count == 0:
+            self.rlock_owner == 0
+            self.lock.release()
+
+    def is_owned_w(self, space):
+        """For internal use by `threading.Condition`."""
+        tid = thread.get_ident()
+        if self.rlock_count > 0 and self.rlock_owner == tid:
+            return space.w_True
+        else:
+            return space.w_False
+
+    @unwrap_spec(count=r_uint, owner=int)
+    def acquire_restore_w(self, space, count, owner):
+        """For internal use by `threading.Condition`."""
+        r = True
+        if not self.lock.acquire(False):
+            r = self.lock.acquire(True)
+        if not r:
+            raise wrap_thread_error(space, "coult not acquire lock")
+        assert self.rlock_count == 0
+        self.rlock_owner = owner
+        self.rlock_count = count
+
+    def release_save_w(self, space):
+        """For internal use by `threading.Condition`."""
+        count, self.rlock_count = self.rlock_count, 0
+        owner, self.rlock_owner = self.rlock_owner, 0
+        return space.newtuple([space.wrap(count), space.wrap(owner)])
+
+    def descr__enter__(self, space):
+        self.acquire_w(space)
+        return self
+
+    def descr__exit__(self, space, *args):
+        self.release_w(space)
+
+W_RLock.typedef = TypeDef(
+    "_thread.RLock",
+    __new__ = interp2app(W_RLock.descr__new__.im_func),
+    acquire = interp2app(W_RLock.acquire_w),
+    release = interp2app(W_RLock.release_w),
+    _is_owned = interp2app(W_RLock.is_owned_w),
+    _acquire_restore = interp2app(W_RLock.acquire_restore_w),
+    _release_save = interp2app(W_RLock.release_save_w),
+    __enter__ = interp2app(W_RLock.descr__enter__),
+    __exit__ = interp2app(W_RLock.descr__exit__),
+    __weakref__ = make_weakref_descr(W_RLock),
+    )
diff --git a/pypy/module/thread/test/test_lock.py b/pypy/module/thread/test/test_lock.py
--- a/pypy/module/thread/test/test_lock.py
+++ b/pypy/module/thread/test/test_lock.py
@@ -80,3 +80,45 @@
 class AppTestLockAgain(GenericTestThread):
     # test it at app-level again to detect strange interactions
     test_lock_again = AppTestLock.test_lock.im_func
+
+
+class AppTestRLock(GenericTestThread):
+    """
+    Tests for recursive locks.
+    """
+    def test_reacquire(self):
+        import _thread
+        lock = _thread.RLock()
+        lock.acquire()
+        lock.acquire()
+        lock.release()
+        lock.acquire()
+        lock.release()
+        lock.release()
+
+    def test_release_unacquired(self):
+        # Cannot release an unacquired lock
+        import _thread
+        lock = _thread.RLock()
+        raises(RuntimeError, lock.release)
+        lock.acquire()
+        lock.acquire()
+        lock.release()
+        lock.acquire()
+        lock.release()
+        lock.release()
+        raises(RuntimeError, lock.release)
+
+    def test__is_owned(self):
+        import _thread
+        lock = _thread.RLock()
+        assert lock._is_owned() is False
+        lock.acquire()
+        assert lock._is_owned() is True
+        lock.acquire()
+        assert lock._is_owned() is True
+        lock.release()
+        assert lock._is_owned() is True
+        lock.release()
+        assert lock._is_owned() is False
+


More information about the pypy-commit mailing list