diff -r 5ce837b1a1d8 Lib/threading.py --- a/Lib/threading.py Tue Feb 26 09:21:10 2013 +0000 +++ b/Lib/threading.py Thu Jun 19 14:04:06 2014 -0600 @@ -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 @@ -12,7 +12,8 @@ from traceback import print_exc as _print_exc # Rename some stuff so "from threading import *" is safe -__all__ = ['activeCount', 'Condition', 'currentThread', 'enumerate', 'Event', +__all__ = ['activeCount', 'active_count', 'Condition', 'currentThread', + 'current_thread', 'enumerate', 'Event', 'Lock', 'RLock', 'Semaphore', 'BoundedSemaphore', 'Thread', 'Timer', 'setprofile', 'settrace', 'local', 'stack_size'] @@ -98,7 +99,7 @@ _thread = self._thread status = ThreadStates[_thread.getState()] if _thread.isDaemon(): status + " daemon" - return "<%s(%s, %s)>" % (self.__class__.__name__, self.getName(), status) + return "<%s(%s, %s %s)>" % (self.__class__.__name__, self.getName(), status, self.ident) def __eq__(self, other): if isinstance(other, JavaThread): @@ -110,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) @@ -124,20 +132,40 @@ else: self._thread.join() + def ident(self): + return self._thread.getId() + + ident = property(ident) + def getName(self): return self._thread.getName() def setName(self, name): self._thread.setName(str(name)) + name = property(getName, setName) + def isAlive(self): return self._thread.isAlive() + is_alive = isAlive + def isDaemon(self): 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): @@ -261,9 +289,13 @@ pythread = JavaThread(jthread) return pythread +current_thread = currentThread + def activeCount(): return len(_threads) +active_count = activeCount + def enumerate(): return _threads.values() @@ -314,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 @@ -385,6 +418,8 @@ def isSet(self): return self.__flag + is_set = isSet + def set(self): self.__cond.acquire() try: @@ -405,5 +440,8 @@ try: if not self.__flag: self.__cond.wait(timeout) + # Issue 2005: Since CPython 2.7, threading.Event.wait(timeout) returns boolean. + # The function should return False if timeout is reached before the event is set. + return self.__flag finally: self.__cond.release() diff -r 5ce837b1a1d8 src/org/python/modules/thread/PyLock.java --- a/src/org/python/modules/thread/PyLock.java Tue Feb 26 09:21:10 2013 +0000 +++ b/src/org/python/modules/thread/PyLock.java Thu Jun 19 14:04:06 2014 -0600 @@ -1,10 +1,9 @@ // Copyright (c) Corporation for National Research Initiatives package org.python.modules.thread; -import org.python.core.Py; -import org.python.core.PyObject; +import org.python.core.*; -public class PyLock extends PyObject { +public class PyLock extends PyObject implements ContextManager { private boolean locked = false; @@ -45,4 +44,16 @@ public boolean locked() { return locked; } + + @Override + public PyObject __enter__(ThreadState ts) { + acquire(); + return this; + } + + @Override + public boolean __exit__(ThreadState ts, PyException exception) { + release(); + return false; + } } diff -r 5ce837b1a1d8 src/org/python/modules/thread/thread.java --- a/src/org/python/modules/thread/thread.java Tue Feb 26 09:21:10 2013 +0000 +++ b/src/org/python/modules/thread/thread.java Thu Jun 19 14:04:06 2014 -0600 @@ -33,7 +33,7 @@ public static void start_new_thread(PyObject func, PyTuple args) { Thread pt = _newFunctionThread(func, args); - PyObject currentThread = func.__findattr__("im_self"); + PyObject currentThread = func.__findattr__("__self__"); if (currentThread != null) { PyObject isDaemon = currentThread.__findattr__("isDaemon"); if (isDaemon != null && isDaemon.isCallable()) { @@ -90,10 +90,9 @@ } public static long get_ident() { - return Py.java_obj_id(Thread.currentThread()); + return Thread.currentThread().getId(); } - public static long stack_size(PyObject[] args) { switch (args.length) { case 0: