Commit 111affcc authored by Jason Madden's avatar Jason Madden

More tests for various uses of locks in a trace function. Make most of them...

More tests for various uses of locks in a trace function. Make most of them work or at least not deadlock.
parent 7b491cea
......@@ -18,37 +18,88 @@ __all__ = ['Semaphore', 'DummySemaphore', 'BoundedSemaphore', 'RLock']
if PYPY:
# TODO: Need to use monkey.get_original?
from thread import allocate_lock as _allocate_lock
from thread import get_ident as _get_ident
_sem_lock = _allocate_lock()
class _OwnedLock(object):
def __init__(self):
self._owner = None
self._block = _allocate_lock()
self._locking = 0
self._count = 0
def untraceable(f):
# Don't allow re-entry to these functions, as can
# happen if a sys.settrace is used
def wrapper(self):
if self._locking:
return
try:
self._locking += 1
return f(self)
finally:
self._locking -= 1
return wrapper
@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 _locked(object):
def __enter__(self):
_sem_lock.acquire()
def __exit__(self, t, v, tb):
_sem_lock.release()
class _unlocked(object):
class _around(object):
before = None
after = None
def __enter__(self):
_sem_lock.release()
self.before()
def __exit__(self, t, v, tb):
_sem_lock.acquire()
self.after()
def _decorate(func, cm):
def _decorate(func, cmname):
# functools.wrap?
def wrapped(*args, **kwargs):
with cm:
return func(*args, **kwargs)
def wrapped(self, *args, **kwargs):
with getattr(self, cmname):
return func(self, *args, **kwargs)
return wrapped
Semaphore._py3k_acquire = Semaphore.acquire = _decorate(Semaphore.acquire, _locked())
Semaphore.release = _decorate(Semaphore.release, _locked())
Semaphore.wait = _decorate(Semaphore.wait, _locked())
Semaphore._do_wait = _decorate(Semaphore._do_wait, _unlocked())
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()
self._lock_locked.before = l.acquire
self._lock_locked.after = l.release
self._lock_unlocked = _around()
self._lock_unlocked.before = l.release
self._lock_unlocked.after = l.acquire
_Sem_init(self, *args, **kwargs)
Semaphore.__init__ = __init__
del _decorate
class DummySemaphore(object):
......
......@@ -78,6 +78,72 @@ class TestTrace(unittest.TestCase):
# Have an assert so that we know if we miscompile
self.assertTrue(len(lst) > 0, "should not compile on pypy")
def test_untraceable_lock_uses_different_lock(self):
if hasattr(sys, 'gettrace'):
old = sys.gettrace()
else:
old = None
PYPY = hasattr(sys, 'pypy_version_info')
lst = []
# we should be able to use unrelated locks from within the trace function
l = allocate_lock()
try:
def trace(frame, ev, arg):
with l:
lst.append((frame.f_code.co_filename, frame.f_lineno, ev))
if not PYPY: # because we expect to trace on PyPy
print("TRACE: %s:%s %s" % lst[-1])
return trace
l2 = allocate_lock()
sys.settrace(trace)
# Separate functions, not the C-implemented `with` so the trace
# function gets a crack at them
l2.acquire()
l2.release()
finally:
sys.settrace(old)
if not PYPY:
self.assertEqual(lst, [], "trace not empty")
else:
# Have an assert so that we know if we miscompile
self.assertTrue(len(lst) > 0, "should not compile on pypy")
def test_untraceable_lock_uses_same_lock(self):
from gevent.hub import LoopExit
if hasattr(sys, 'gettrace'):
old = sys.gettrace()
else:
old = None
PYPY = hasattr(sys, 'pypy_version_info')
lst = []
e = None
# we should not be able to use the same lock from within the trace function
# because it's over acquired but instead of deadlocking it raises an exception
l = allocate_lock()
try:
def trace(frame, ev, arg):
with l:
lst.append((frame.f_code.co_filename, frame.f_lineno, ev))
return trace
sys.settrace(trace)
# Separate functions, not the C-implemented `with` so the trace
# function gets a crack at them
l.acquire()
except LoopExit as ex:
e = ex
finally:
sys.settrace(old)
if not PYPY:
self.assertEqual(lst, [], "trace not empty")
else:
# Have an assert so that we know if we miscompile
self.assertTrue(len(lst) > 0, "should not compile on pypy")
self.assertTrue(isinstance(e, LoopExit))
def run_script(self, more_args=()):
args = [sys.executable, "-c", script]
args.extend(more_args)
......
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