[Jython-checkins] jython: Fix various threading issues introduced by previous fix

jim.baker jython-checkins at python.org
Sun Apr 19 20:33:59 CEST 2015


https://hg.python.org/jython/rev/dd4327161042
changeset:   7678:dd4327161042
user:        Jim Baker <jim.baker at rackspace.com>
date:        Sun Apr 19 12:33:38 2015 -0600
summary:
  Fix various threading issues introduced by previous fix

repr() of threading.{Condition,Lock,RLock} is now a cleaned-up version
of what is seen in CPython. Example for an acquired RLock:
<_threading.RLock owner='MainThread' count=1>. But we differ from
CPython in providing something similar for Lock: <_threading.Lock
owner='MainThread' locked=True>

Fixed typo such that creating a new RLock in Python would actually
return a Lock, which has quite different semantics in that it's not
actually reentrant.

Awaiting conditions may return early in general, although spurious
notifies are rare in the usual happy path. However, to avoid flakiness
in the test, do not expect the time to be exact. This requires
patching Lib/lock_tests.py specifically for this scenario.

files:
  CoreExposed.includes                                           |   1 +
  Lib/test/lock_tests.py                                         |   8 +-
  Lib/test/test_threading_jy.py                                  |  35 ++++-
  src/org/python/modules/_threading/Condition.java               |  15 ++
  src/org/python/modules/_threading/ConditionSupportingLock.java |   1 +
  src/org/python/modules/_threading/Lock.java                    |  51 ++++++++-
  src/org/python/modules/_threading/RLock.java                   |  43 ++++++-
  7 files changed, 124 insertions(+), 30 deletions(-)


diff --git a/CoreExposed.includes b/CoreExposed.includes
--- a/CoreExposed.includes
+++ b/CoreExposed.includes
@@ -105,6 +105,7 @@
 org/python/modules/jffi/StructLayout$ScalarField.class
 org/python/modules/_threading/Condition.class
 org/python/modules/_threading/Lock.class
+org/python/modules/_threading/RLock.class
 org/python/modules/_weakref/CallableProxyType.class
 org/python/modules/_weakref/ProxyType.class
 org/python/modules/_weakref/ReferenceType.class
diff --git a/Lib/test/lock_tests.py b/Lib/test/lock_tests.py
--- a/Lib/test/lock_tests.py
+++ b/Lib/test/lock_tests.py
@@ -291,7 +291,13 @@
         self.assertEqual(results1, [False] * N)
         for r, dt in results2:
             self.assertFalse(r)
-            self.assertTrue(dt >= 0.2, dt)
+            # Fudge factor for running on the JVM - actual waits on
+            # some OS platforms might be like this example,
+            # 0.199999809265, slightly less than 0.2 seconds. To avoid
+            # unnecessary flakiness in testing, make epsilon
+            # relatively large:
+            epsilon = 0.01
+            self.assertTrue(dt >= 0.2 - epsilon, dt)
         # The event is set
         results1 = []
         results2 = []
diff --git a/Lib/test/test_threading_jy.py b/Lib/test/test_threading_jy.py
--- a/Lib/test/test_threading_jy.py
+++ b/Lib/test/test_threading_jy.py
@@ -97,22 +97,37 @@
         self.assertEqual(joined_threads, num_threads)
 
 
-class MemoryLeakTestCase(unittest.TestCase):
-    def test_socket_server(self):
-        # run socketserver with a small amount of memory; verify it exits cleanly
+class ReprTestCase(unittest.TestCase):
 
-        
-        rc = subprocess.call([sys.executable,
-                   "-J-Xmx32m",
-                   test_support.findfile("socketserver_test.py")])
-        # stdout=PIPE)
-        self.assertEquals(rc, 0)
+    def test_condition(self):
+        l = Lock()
+        c = Condition(l)
+        self.assertEqual(repr(c), "<_threading.Condition(<_threading.Lock owner=None locked=False>), 0")
+        l.acquire()
+        self.assertEqual(repr(c), "<_threading.Condition(<_threading.Lock owner='MainThread' locked=True>), 0")
+    
+    def test_lock(self):
+        l = Lock()
+        self.assertEqual(repr(l), "<_threading.Lock owner=None locked=False>")
+        r.acquire()
+        self.assertEqual(repr(r), "<_threading.RLock owner='MainThread' locked=True>")
+        r.release()
+        self.assertEqual(repr(r), "<_threading.RLock owner=None locked=False>")
+
+    def test_rlock(self):
+        r = RLock()
+        self.assertEqual(repr(r), "<_threading.RLock owner=None count=0>")
+        r.acquire()
+        self.assertEqual(repr(r), "<_threading.RLock owner='MainThread' count=1>")
+        r.acquire()
+        self.assertEqual(repr(r), "<_threading.RLock owner='MainThread' count=2>")
+        r.release(); r.release()
+        self.assertEqual(repr(r), "<_threading.RLock owner=None count=0>")
 
 
 def test_main():
     test_support.run_unittest(
         JavaIntegrationTestCase,
-        #MemoryLeakTestCase,
         ThreadingTestCase,
         TwistedTestCase)
 
diff --git a/src/org/python/modules/_threading/Condition.java b/src/org/python/modules/_threading/Condition.java
--- a/src/org/python/modules/_threading/Condition.java
+++ b/src/org/python/modules/_threading/Condition.java
@@ -5,6 +5,8 @@
 import org.python.core.PyException;
 import org.python.core.PyNewWrapper;
 import org.python.core.PyObject;
+import org.python.core.PyString;
+import org.python.core.PyTuple;
 import org.python.core.PyType;
 import org.python.core.ThreadState;
 import org.python.expose.ExposedType;
@@ -143,6 +145,19 @@
         return _lock._is_owned();
     }
 
+    @Override
+    public String toString() {
+        int count = 0;
+        try {
+            count = _lock.getWaitQueueLength(_condition);
+        } catch (IllegalMonitorStateException ex) {
+            // ignore if lock is not held
+        }
+        return (Py.newString("<_threading.Condition(%s, %d)>").__mod__(
+                new PyTuple(
+                        Py.newString(_lock.toString()),
+                        Py.newInteger(count)))).toString();
+    }
 
     /* Traverseproc implementation */
     @Override
diff --git a/src/org/python/modules/_threading/ConditionSupportingLock.java b/src/org/python/modules/_threading/ConditionSupportingLock.java
--- a/src/org/python/modules/_threading/ConditionSupportingLock.java
+++ b/src/org/python/modules/_threading/ConditionSupportingLock.java
@@ -11,4 +11,5 @@
     boolean acquire(boolean blocking);
     void release();
     boolean _is_owned();
+    int	getWaitQueueLength(java.util.concurrent.locks.Condition condition);
 }
diff --git a/src/org/python/modules/_threading/Lock.java b/src/org/python/modules/_threading/Lock.java
--- a/src/org/python/modules/_threading/Lock.java
+++ b/src/org/python/modules/_threading/Lock.java
@@ -3,9 +3,11 @@
 import java.util.concurrent.locks.AbstractQueuedSynchronizer;
 import java.util.concurrent.TimeUnit;
 import org.python.core.ContextManager;
+import org.python.core.Py;
 import org.python.core.PyException;
 import org.python.core.PyNewWrapper;
 import org.python.core.PyObject;
+import org.python.core.PyTuple;
 import org.python.core.PyType;
 import org.python.core.ThreadState;
 import org.python.core.Untraversable;
@@ -21,10 +23,9 @@
     // see http://bugs.jython.org/issue2328 - need to support another thread
     // releasing this lock, per CPython semantics, so support semantics with
     // custom non-reentrant lock, not a ReentrantLock
-    private final Mutex _lock;
+    private final Mutex _lock = new Mutex();
 
     public Lock() {
-        this._lock = new Mutex();
     }
 
     public java.util.concurrent.locks.Lock getLock() {
@@ -105,6 +106,19 @@
         return Lock__is_owned();
     }
 
+    public int getWaitQueueLength(java.util.concurrent.locks.Condition condition) {
+        return _lock.getWaitQueueLength(condition);
+    }
+
+    @ExposedMethod
+    public String toString() {
+        String owner = _lock.getOwnerName();
+        return Py.newString("<_threading.Lock owner=%r locked=%s>").
+                __mod__(new PyTuple(
+                        owner != null ? Py.newStringOrUnicode(owner) : Py.None,
+                        Py.newBoolean(_lock.isLocked()))).toString();
+    }
+
 }
 
 
@@ -128,16 +142,22 @@
 		}
 
 		// Releases the lock by setting state to zero
-		protected boolean tryRelease(int releases) {
-			assert releases == 1; // Otherwise unused
-			if (getState() == 0) throw new IllegalMonitorStateException();
-			setExclusiveOwnerThread(null);
-			setState(0);
-			return true;
-		}
+        protected boolean tryRelease(int releases) {
+            assert releases == 1; // Otherwise unused
+            if (getState() == 0) {
+                throw new IllegalMonitorStateException();
+            }
+            setExclusiveOwnerThread(null);
+            setState(0);
+            return true;
+        }
 
 		// Provides a Condition
 		ConditionObject newCondition() { return new ConditionObject(); }
+
+        Thread getOwner() {
+            return getExclusiveOwnerThread();
+        }
 	}
 
 	// The sync object does all the hard work. We just forward to it.
@@ -156,4 +176,17 @@
 		throws InterruptedException {
 		return sync.tryAcquireNanos(1, unit.toNanos(timeout));
 	}
+    public int getWaitQueueLength(java.util.concurrent.locks.Condition condition) {
+        if (condition instanceof AbstractQueuedSynchronizer.ConditionObject) {
+            return sync.getWaitQueueLength((AbstractQueuedSynchronizer.ConditionObject) condition);
+        } else {
+            // punt, no estimate available, but this should never occur using
+            // standard locks and conditions from this module
+            return 0;
+        }
+    }
+    String getOwnerName() {
+        Thread owner = sync.getOwner();
+        return owner != null ? owner.getName() : null;
+    }
 }
diff --git a/src/org/python/modules/_threading/RLock.java b/src/org/python/modules/_threading/RLock.java
--- a/src/org/python/modules/_threading/RLock.java
+++ b/src/org/python/modules/_threading/RLock.java
@@ -1,11 +1,13 @@
 package org.python.modules._threading;
 
-import java.util.concurrent.locks.ReentrantLock;
+
 import org.python.core.ContextManager;
 import org.python.core.Py;
 import org.python.core.PyException;
 import org.python.core.PyNewWrapper;
 import org.python.core.PyObject;
+import org.python.core.PyString;
+import org.python.core.PyTuple;
 import org.python.core.PyType;
 import org.python.core.ThreadState;
 import org.python.core.Untraversable;
@@ -18,24 +20,22 @@
 public class RLock extends PyObject implements ContextManager, ConditionSupportingLock {
 
     public static final PyType TYPE = PyType.fromClass(RLock.class);
-    private final ReentrantLock _lock;
+    private final RLockImplementation _lock = new RLockImplementation();
 
     public RLock() {
-        this._lock = new ReentrantLock();
     }
 
-	public java.util.concurrent.locks.Lock getLock() {
-		return _lock;
-	}
+    public java.util.concurrent.locks.Lock getLock() {
+        return _lock;
+    }
 
     @ExposedNew
-    final static PyObject RLock___new__ (PyNewWrapper new_, boolean init,
-            PyType subtype, PyObject[] args, String[] keywords) {
+    final static PyObject RLock___new__(PyNewWrapper new_, boolean init,
+                                        PyType subtype, PyObject[] args, String[] keywords) {
         final int nargs = args.length;
-        return new Lock();
+        return new RLock();
     }
 
-
     @ExposedMethod(defaults = "true")
     final boolean RLock_acquire(boolean blocking) {
         if (blocking) {
@@ -105,4 +105,27 @@
     public boolean _is_owned() {
         return RLock__is_owned();
     }
+
+    public int getWaitQueueLength(java.util.concurrent.locks.Condition condition) {
+        return _lock.getWaitQueueLength(condition);
+    }
+
+    @ExposedMethod
+    public String toString() {
+        String owner = _lock.getOwnerName();
+        return Py.newString("<_threading.RLock owner=%r count=%d>").
+                __mod__(new PyTuple(
+                        owner != null ? Py.newStringOrUnicode(owner) : Py.None,
+                        Py.newInteger(_lock.getHoldCount()))).toString();
+    }
 }
+
+
+final class RLockImplementation extends java.util.concurrent.locks.ReentrantLock {
+    String getOwnerName() {
+        Thread owner = getOwner();
+        return owner != null ? owner.getName() : null;
+    }
+}
+
+

-- 
Repository URL: https://hg.python.org/jython


More information about the Jython-checkins mailing list