[Jython-checkins] jython: Fixed bugs corresponding to test_threading skips for Jython, as

jim.baker jython-checkins at python.org
Tue Apr 15 21:58:56 CEST 2014


http://hg.python.org/jython/rev/b236e2db7a80
changeset:   7204:b236e2db7a80
user:        Jim Baker <jim.baker at rackspace.com>
date:        Tue Apr 15 13:58:53 2014 -0600
summary:
  Fixed bugs corresponding to test_threading skips for Jython, as
well as bug 2125 (missing threading.Thread.daemon attribute).

files:
  Lib/test/lock_tests.py                           |  23 +++-----
  Lib/test/test_threading.py                       |  19 +-----
  Lib/threading.py                                 |  27 ++++++++-
  src/org/python/modules/_threading/Condition.java |  26 +++++++--
  src/org/python/modules/_threading/Lock.java      |   4 +-
  5 files changed, 57 insertions(+), 42 deletions(-)


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
@@ -149,7 +149,7 @@
     Tests for non-recursive, weak locks
     (which can be acquired and released from different threads).
     """
-    @unittest.skipIf(support.is_jython, "FIXME: not working properly on Jython")
+    @unittest.skipIf(support.is_jython, "Jython only supports recursive locks")
     def test_reacquire(self):
         # Lock needs to be released before re-acquiring.
         lock = self.locktype()
@@ -169,6 +169,7 @@
             _wait()
         self.assertEqual(len(phase), 2)
 
+    @unittest.skipIf(support.is_jython, "Java does not allow locks to be released from different threads")
     def test_different_thread(self):
         # Lock can be released from a different thread.
         lock = self.locktype()
@@ -194,7 +195,6 @@
         lock.release()
         lock.release()
 
-    @unittest.skipIf(support.is_jython, "FIXME: not working properly on Jython")
     def test_release_unacquired(self):
         # Cannot release an unacquired lock
         lock = self.locktype()
@@ -207,7 +207,6 @@
         lock.release()
         self.assertRaises(RuntimeError, lock.release)
 
-    @unittest.skipIf(support.is_jython, "FIXME: not working properly on Jython")
     def test_different_thread(self):
         # Cannot release from a different thread
         lock = self.locktype()
@@ -271,7 +270,6 @@
         self.assertEqual(results1, [True] * N)
         self.assertEqual(results2, [True] * N)
 
-    @unittest.skipIf(support.is_jython, "FIXME: not working properly on Jython")
     def test_notify(self):
         evt = self.eventtype()
         self._check_notify(evt)
@@ -280,7 +278,6 @@
         evt.clear()
         self._check_notify(evt)
 
-    @unittest.skipIf(support.is_jython, "FIXME: not working properly on Jython")
     def test_timeout(self):
         evt = self.eventtype()
         results1 = []
@@ -294,9 +291,10 @@
             results2.append((r, t2 - t1))
         Bunch(f, N).wait_for_finished()
         self.assertEqual(results1, [False] * N)
+        epsilon = 1e-5  # wait time is hard to test precisely, so keep low resolution
         for r, dt in results2:
             self.assertFalse(r)
-            self.assertTrue(dt >= 0.2, dt)
+            self.assertTrue(dt >= (0.2 - epsilon), dt)
         # The event is set
         results1 = []
         results2 = []
@@ -312,7 +310,6 @@
     Tests for condition variables.
     """
 
-    @unittest.skipIf(support.is_jython, "FIXME: not working properly on Jython")
     def test_acquire(self):
         cond = self.condtype()
         # Be default we have an RLock: the condition can be acquired multiple
@@ -324,20 +321,18 @@
         lock = threading.Lock()
         cond = self.condtype(lock)
         cond.acquire()
-        self.assertFalse(lock.acquire(False))
+        self.assertTrue(lock.acquire(False))  # All locks in Jython are recursive!
         cond.release()
         self.assertTrue(lock.acquire(False))
-        self.assertFalse(cond.acquire(False))
+        self.assertTrue(cond.acquire(False))  # All locks in Jython are recursive!
         lock.release()
         with cond:
-            self.assertFalse(lock.acquire(False))
+            self.assertTrue(lock.acquire(False))  # All locks in Jython are recursive!
 
-    @unittest.skipIf(support.is_jython, "FIXME: not working properly on Jython")
     def test_unacquired_wait(self):
         cond = self.condtype()
         self.assertRaises(RuntimeError, cond.wait)
 
-    @unittest.skipIf(support.is_jython, "FIXME: not working properly on Jython")
     def test_unacquired_notify(self):
         cond = self.condtype()
         self.assertRaises(RuntimeError, cond.notify)
@@ -411,7 +406,6 @@
         # A second time, to check internal state is still ok.
         self._check_notify(cond)
 
-    @unittest.skipIf(support.is_jython, "FIXME: not working properly on Jython")
     def test_timeout(self):
         cond = self.condtype()
         results = []
@@ -425,8 +419,9 @@
             results.append(t2 - t1)
         Bunch(f, N).wait_for_finished()
         self.assertEqual(len(results), 5)
+        epsilon = 1e-5  # wait time is hard to test precisely, so keep low resolution
         for dt in results:
-            self.assertTrue(dt >= 0.2, dt)
+            self.assertTrue(dt >= (0.2 - epsilon), dt)
 
 
 class BaseSemaphoreTests(BaseTestCase):
diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py
--- a/Lib/test/test_threading.py
+++ b/Lib/test/test_threading.py
@@ -102,7 +102,6 @@
             print 'all tasks done'
         self.assertEqual(numrunning.get(), 0)
 
-    @unittest.skipIf(is_jython, "FIXME: not working on Jython")
     def test_ident_of_no_threading_threads(self):
         # The ident still must work for the main thread and dummy threads.
         self.assertFalse(threading.currentThread().ident is None)
@@ -118,7 +117,6 @@
         del threading._active[ident[0]]
 
     # run with a small(ish) thread stack size (256kB)
-    @unittest.skipIf(is_jython, "FIXME: not working on Jython")
     def test_various_ops_small_stack(self):
         if verbose:
             print 'with 256kB thread stack size...'
@@ -132,7 +130,6 @@
         threading.stack_size(0)
 
     # run with a large thread stack size (1MB)
-    @unittest.skipIf(is_jython, "FIXME: not working on Jython")
     def test_various_ops_large_stack(self):
         if verbose:
             print 'with 1MB thread stack size...'
@@ -256,7 +253,7 @@
             t.join()
         # else the thread is still running, and we have no way to kill it
 
-    @unittest.skipIf(is_jython, "FIXME: not working properly on Jython")
+    @unittest.skipIf(is_jython, "Does not apply to Jython")
     def test_limbo_cleanup(self):
         # Issue 7481: Failure to start thread should cleanup the limbo map.
         def fail_new_thread(*args):
@@ -272,7 +269,7 @@
         finally:
             threading._start_new_thread = _start_new_thread
 
-    @unittest.skipIf(is_jython, "FIXME: investigate on Jython")
+    @unittest.skipIf(is_jython, "Does not apply to Jython")
     def test_finalize_runnning_thread(self):
         # Issue 1402: the PyGILState_Ensure / _Release functions may be called
         # very late on python exit: on deallocation of a running thread for
@@ -311,7 +308,6 @@
             """])
         self.assertEqual(rc, 42)
 
-    @unittest.skipIf(is_jython, "FIXME: investigate on Jython")
     def test_finalize_with_trace(self):
         # Issue1733757
         # Avoid a deadlock when sys.settrace steps into threading._shutdown
@@ -346,7 +342,6 @@
         self.assertTrue(rc == 0,
                         "Unexpected error: " + repr(stderr))
 
-    @unittest.skipIf(is_jython, "FIXME: investigate on Jython")
     def test_join_nondaemon_on_shutdown(self):
         # Issue 1722344
         # Raising SystemExit skipped threading._shutdown
@@ -368,8 +363,8 @@
         self.addCleanup(p.stdout.close)
         self.addCleanup(p.stderr.close)
         stdout, stderr = p.communicate()
-        self.assertEqual(stdout.strip(),
-            "Woke up, sleep function is: <built-in function sleep>")
+        self.assertTrue(stdout.strip().startswith(
+            "Woke up, sleep function is: <java function sleep"))
         stderr = re.sub(r"^\[\d+ refs\]", "", stderr, re.MULTILINE).strip()
         self.assertEqual(stderr, "")
 
@@ -676,23 +671,19 @@
 class ThreadingExceptionTests(BaseTestCase):
     # A RuntimeError should be raised if Thread.start() is called
     # multiple times.
-    @unittest.skipIf(is_jython, "FIXME: not working properly on Jython")
     def test_start_thread_again(self):
         thread = threading.Thread()
         thread.start()
         self.assertRaises(RuntimeError, thread.start)
 
-    @unittest.skipIf(is_jython, "FIXME: investigate on Jython")
     def test_joining_current_thread(self):
         current_thread = threading.current_thread()
         self.assertRaises(RuntimeError, current_thread.join);
 
-    @unittest.skipIf(is_jython, "FIXME: investigate on Jython")
     def test_joining_inactive_thread(self):
         thread = threading.Thread()
         self.assertRaises(RuntimeError, thread.join)
 
-    @unittest.skipIf(is_jython, "FIXME: investigate on Jython")
     def test_daemonize_active_thread(self):
         thread = threading.Thread()
         thread.start()
@@ -708,7 +699,6 @@
 class EventTests(lock_tests.EventTests):
     eventtype = staticmethod(threading.Event)
 
- at unittest.skipIf(is_jython, "FIXME: investigate on Jython")
 class ConditionAsRLockTests(lock_tests.RLockTests):
     # An Condition uses an RLock by default and exports its API.
     locktype = staticmethod(threading.Condition)
@@ -719,7 +709,6 @@
 class SemaphoreTests(lock_tests.SemaphoreTests):
     semtype = staticmethod(threading.Semaphore)
 
- at unittest.skipIf(is_jython, "FIXME: investigate on Jython")
 class BoundedSemaphoreTests(lock_tests.BoundedSemaphoreTests):
     semtype = staticmethod(threading.BoundedSemaphore)
 
diff --git a/Lib/threading.py b/Lib/threading.py
--- a/Lib/threading.py
+++ b/Lib/threading.py
@@ -1,4 +1,4 @@
-from java.lang import InterruptedException
+from java.lang import IllegalThreadStateException, InterruptedException
 from java.util import Collections, WeakHashMap
 from java.util.concurrent import Semaphore, CyclicBarrier
 from java.util.concurrent.locks import ReentrantLock
@@ -111,12 +111,19 @@
         return not self.__eq__(other)
 
     def start(self):
-        self._thread.start()
+        try:
+            self._thread.start()
+        except IllegalThreadStateException:
+            raise RuntimeError("threads can only be started once")
 
     def run(self):
         self._thread.run()
 
     def join(self, timeout=None):
+        if self._thread == java.lang.Thread.currentThread():
+            raise RuntimeError("cannot join current thread")
+        elif self._thread.getState() == java.lang.Thread.State.NEW:
+            raise RuntimeError("cannot join thread before it is started")
         if timeout:
             millis = timeout * 1000.
             millis_int = int(millis)
@@ -147,7 +154,18 @@
         return self._thread.isDaemon()
 
     def setDaemon(self, daemonic):
-        self._thread.setDaemon(bool(daemonic))
+        if self._thread.getState() != java.lang.Thread.State.NEW:
+            # thread could in fact be dead... Python uses the same error
+            raise RuntimeError("cannot set daemon status of active thread")
+        try:
+            self._thread.setDaemon(bool(daemonic))
+        except IllegalThreadStateException:
+            # changing daemonization only makes sense in Java when the
+            # thread is alive; need extra test on the exception
+            # because of possible races on interrogating with getState
+            raise RuntimeError("cannot set daemon status of active thread")
+
+    daemon = property(isDaemon, setDaemon)
 
     def __tojava__(self, c):
         if isinstance(self._thread, c):
@@ -328,7 +346,8 @@
     # After Tim Peters' semaphore class, but not quite the same (no maximum)
 
     def __init__(self, value=1, verbose=None):
-        assert value >= 0, "Semaphore initial value must be >= 0"
+        if value < 0:
+            raise ValueError("Semaphore initial value must be >= 0")
         _Verbose.__init__(self, verbose)
         self.__cond = Condition(Lock())
         self.__value = value
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
@@ -87,11 +87,15 @@
 
     @ExposedMethod(defaults = "Py.None")
     final void Condition_wait(PyObject timeout) throws InterruptedException {
-        if (timeout == Py.None) {
-            _condition.await();
-        } else {
-            long nanos = (long) (timeout.asDouble() * 1e9);
-            _condition.awaitNanos(nanos);
+        try {
+            if (timeout == Py.None) {
+                _condition.await();
+            } else {
+                long nanos = (long) (timeout.asDouble() * 1e9);
+                _condition.awaitNanos(nanos);
+            }
+        } catch (IllegalMonitorStateException ex) {
+            throw Py.RuntimeError("cannot wait on un-acquired lock");
         }
     }
 
@@ -101,7 +105,11 @@
 
     @ExposedMethod
     final void Condition_notify() {
-        _condition.signal();
+        try {
+            _condition.signal();
+        } catch (IllegalMonitorStateException ex) {
+            throw Py.RuntimeError("cannot notify on un-acquired lock");
+        }
     }
 
     public void notifyAll$() {
@@ -110,7 +118,11 @@
 
     @ExposedMethod
     final void Condition_notifyAll() {
-        _condition.signalAll();
+        try {
+            _condition.signalAll();
+        } catch (IllegalMonitorStateException ex) {
+            throw Py.RuntimeError("cannot notify on un-acquired lock");
+        }
     }
 
     @ExposedMethod
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
@@ -61,8 +61,8 @@
 
     @ExposedMethod
     final void Lock_release() {
-        if (!_lock.isHeldByCurrentThread()) {
-            throw Py.AssertionError("release() of un-acquire()d lock");
+        if (!_lock.isHeldByCurrentThread() || _lock.getHoldCount() <= 0) {
+            throw Py.RuntimeError("cannot release un-acquired lock");
         }
         _lock.unlock();
     }

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


More information about the Jython-checkins mailing list