summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorQuan Tian <qtian@vmware.com>2017-05-09 00:34:33 -0700
committerQuan Tian <qtian@vmware.com>2017-05-10 19:32:43 -0700
commitf72cc96a70f9e9d23fc6e00d994263829b672be0 (patch)
tree78a65d5b9ea3fa9f128cd60fed9185106e5a47b9
parent7f53465578543156e7251e243c0636e087a8445f (diff)
downloadeventlet-f72cc96a70f9e9d23fc6e00d994263829b672be0.tar.gz
patcher: set locked RLocks' owner only when patching existing locks
For Python 2, patching existing locks replaces RLock._RLock__owner with current thread ID no matter the old lock is locked or not and thus an unlocked RLock would have a non None owner (e.g. <_RLock owner=140106584489808 count=0>). Then if we acquire the RLock in the same thread, the method RLock.acquire would not invoke the _RLock__block.acquire() since it treats this is a recursive acquire by checking current thread ID. And then the following RLock.release would invoke the _RLock__block.release method resulting in the counter of Semaphore being improved to 2. There should be only two states being expected for RLock: 1. owner != None and count > 0 2. owner == None and count == 0 This patch fixs it by only setting locked RLocks' owner during patching.
-rw-r--r--eventlet/patcher.py2
-rw-r--r--tests/__init__.py9
-rw-r--r--tests/isolated/patcher_existing_locks_unlocked.py25
-rw-r--r--tests/patcher_test.py5
4 files changed, 40 insertions, 1 deletions
diff --git a/eventlet/patcher.py b/eventlet/patcher.py
index c0ef377..a929f7f 100644
--- a/eventlet/patcher.py
+++ b/eventlet/patcher.py
@@ -358,7 +358,7 @@ def _fix_py2_rlock(rlock, tid):
rlock._RLock__block = new
if old.locked():
new.acquire()
- rlock._RLock__owner = tid
+ rlock._RLock__owner = tid
def _fix_py3_rlock(old):
diff --git a/tests/__init__.py b/tests/__init__.py
index f4b806e..7ebb811 100644
--- a/tests/__init__.py
+++ b/tests/__init__.py
@@ -123,6 +123,15 @@ def skip_if_no_itimer(func):
return skip_unless(has_itimer)(func)
+def skip_if_CRLock_exist(func):
+ """ Decorator that skips a test if the `_thread.RLock` class exists """
+ try:
+ from _thread import RLock
+ return skipped(func)
+ except ImportError:
+ return func
+
+
def skip_if_no_ssl(func):
""" Decorator that skips a test if SSL is not available."""
try:
diff --git a/tests/isolated/patcher_existing_locks_unlocked.py b/tests/isolated/patcher_existing_locks_unlocked.py
new file mode 100644
index 0000000..e6f1a91
--- /dev/null
+++ b/tests/isolated/patcher_existing_locks_unlocked.py
@@ -0,0 +1,25 @@
+__test__ = False
+
+
+def take(lock, e1, e2):
+ with lock:
+ e1.set()
+ e2.wait()
+
+
+if __name__ == '__main__':
+ import sys
+ import threading
+ lock = threading.RLock()
+ import eventlet
+ eventlet.monkey_patch()
+
+ lock.acquire()
+ lock.release()
+
+ e1, e2 = threading.Event(), threading.Event()
+ eventlet.spawn(take, lock, e1, e2)
+ e1.wait()
+ assert not lock.acquire(blocking=0)
+ e2.set()
+ print('pass')
diff --git a/tests/patcher_test.py b/tests/patcher_test.py
index eb8e100..ff59400 100644
--- a/tests/patcher_test.py
+++ b/tests/patcher_test.py
@@ -493,6 +493,11 @@ def test_patcher_existing_locks_locked():
tests.run_isolated('patcher_existing_locks_locked.py')
+@tests.skip_if_CRLock_exist
+def test_patcher_existing_locks_unlocked():
+ tests.run_isolated('patcher_existing_locks_unlocked.py')
+
+
def test_importlib_lock():
tests.run_isolated('patcher_importlib_lock.py')