[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