From: <le...@us...> - 2008-08-07 02:27:11
|
Revision: 5100 http://jython.svn.sourceforge.net/jython/?rev=5100&view=rev Author: leosoto Date: 2008-08-07 02:27:08 +0000 (Thu, 07 Aug 2008) Log Message: ----------- Experimental interpreter restarting support, aimed at webapp dev-server 'reloading', without having to resort to process forking. There is more info here: <http://codereview.appspot.com/2810>, where I posted the first attempt of this patch Modified Paths: -------------- branches/asm/Lib/threading.py branches/asm/src/org/python/core/FunctionThread.java branches/asm/src/org/python/core/PyTableCode.java branches/asm/src/org/python/modules/Setup.java branches/asm/src/org/python/modules/thread/thread.java branches/asm/src/org/python/util/jython.java Added Paths: ----------- branches/asm/src/org/python/modules/_systemrestart.java Modified: branches/asm/Lib/threading.py =================================================================== --- branches/asm/Lib/threading.py 2008-08-06 23:51:03 UTC (rev 5099) +++ branches/asm/Lib/threading.py 2008-08-07 02:27:08 UTC (rev 5100) @@ -1,6 +1,6 @@ from java.util.concurrent import Semaphore, CyclicBarrier from java.util.concurrent.locks import ReentrantLock -from org.python.core import FunctionThread +from thread import _newFunctionThread from thread import _local as local import java.lang.Thread import weakref @@ -227,7 +227,7 @@ _threads[_thread.getId()] = self def _create_thread(self): - return FunctionThread(self.__bootstrap, ()) + return _newFunctionThread(self.__bootstrap, ()) def run(self): if self._target: Modified: branches/asm/src/org/python/core/FunctionThread.java =================================================================== --- branches/asm/src/org/python/core/FunctionThread.java 2008-08-06 23:51:03 UTC (rev 5099) +++ branches/asm/src/org/python/core/FunctionThread.java 2008-08-07 02:27:08 UTC (rev 5100) @@ -1,31 +1,27 @@ package org.python.core; - + +import org.python.modules._systemrestart; + public class FunctionThread extends Thread { private final PyObject func; private final PyObject[] args; private final PySystemState systemState; - public FunctionThread(PyObject func, PyObject[] args) { - super(); + public FunctionThread(PyObject func, PyObject[] args, long stack_size, ThreadGroup group) { + super(group, null, "Thread", stack_size); this.func = func; this.args = args; this.systemState = Py.getSystemState(); } - - public FunctionThread(PyObject func, PyObject[] args, long stack_size) { - super(null, null, "Thread", stack_size); - this.func = func; - this.args = args; - this.systemState = Py.getSystemState(); - } public void run() { Py.setSystemState(systemState); try { func.__call__(args); } catch (PyException exc) { - if (Py.matchException(exc, Py.SystemExit)) { + if (Py.matchException(exc, Py.SystemExit) || + Py.matchException(exc, _systemrestart.SystemRestart)) { return; } Py.stderr.println("Unhandled exception in thread started by " + func); Modified: branches/asm/src/org/python/core/PyTableCode.java =================================================================== --- branches/asm/src/org/python/core/PyTableCode.java 2008-08-06 23:51:03 UTC (rev 5099) +++ branches/asm/src/org/python/core/PyTableCode.java 2008-08-07 02:27:08 UTC (rev 5100) @@ -1,6 +1,8 @@ // Copyright (c) Corporation for National Research Initiatives package org.python.core; +import org.python.modules._systemrestart; + /** * An implementation of PyCode where the actual executable content * is stored as a PyFunctionTable instance and an integer index. @@ -226,6 +228,12 @@ ts.exception = previous_exception; ts.frame = ts.frame.f_back; + + // Check for interruption, which is used for restarting the interpreter + // on Jython + if (Thread.currentThread().isInterrupted()) { + throw new PyException(_systemrestart.SystemRestart); + } return ret; } Modified: branches/asm/src/org/python/modules/Setup.java =================================================================== --- branches/asm/src/org/python/modules/Setup.java 2008-08-06 23:51:03 UTC (rev 5099) +++ branches/asm/src/org/python/modules/Setup.java 2008-08-07 02:27:08 UTC (rev 5100) @@ -54,6 +54,7 @@ "gc", "_hashlib", "_functools:org.python.modules._functools._functools", - "_csv:org.python.modules._csv._csv" + "_csv:org.python.modules._csv._csv", + "_systemrestart" }; } Added: branches/asm/src/org/python/modules/_systemrestart.java =================================================================== --- branches/asm/src/org/python/modules/_systemrestart.java (rev 0) +++ branches/asm/src/org/python/modules/_systemrestart.java 2008-08-07 02:27:08 UTC (rev 5100) @@ -0,0 +1,29 @@ +package org.python.modules; + +import org.python.core.ClassDictInit; +import org.python.core.Py; +import org.python.core.PyException; +import org.python.core.PyObject; +import org.python.core.PyStringMap; + +public class _systemrestart implements ClassDictInit { + /** + * Jython-specific exception for restarting the interpreter. Currently + * supported only by jython.java, when executing a file (i.e, + * non-interactive mode). + * + * WARNING: This is highly *experimental* and subject to change. + */ + public static PyObject SystemRestart; + + public static void classDictInit(PyObject dict) { + SystemRestart = Py.makeClass( + "_systemrestart.SystemRestart", Py.BaseException, + new PyStringMap() {{ + __setitem__("__doc__", + Py.newString("Request to restart the interpreter. " + + "(Jython-specific)")); + }}); + dict.__delitem__("classDictInit"); + } +} Modified: branches/asm/src/org/python/modules/thread/thread.java =================================================================== --- branches/asm/src/org/python/modules/thread/thread.java 2008-08-06 23:51:03 UTC (rev 5099) +++ branches/asm/src/org/python/modules/thread/thread.java 2008-08-07 02:27:08 UTC (rev 5100) @@ -8,13 +8,16 @@ import org.python.core.PyInteger; import org.python.core.PyObject; import org.python.core.PyString; +import org.python.core.PyTableCode; import org.python.core.PyType; import org.python.core.PyTuple; public class thread implements ClassDictInit { private static volatile long stack_size = 0; // XXX - can we figure out the current stack size? - + private static ThreadGroup group = new ThreadGroup("jython-threads"); + + public static PyString __doc__ = new PyString( "This module provides primitive operations to write multi-threaded "+ "programs.\n" + @@ -24,12 +27,13 @@ public static void classDictInit(PyObject dict) { dict.__setitem__("LockType", PyType.fromClass(PyLock.class)); dict.__setitem__("_local", PyLocal.TYPE); + dict.__setitem__("interruptAllThreads", null); } public static PyObject error = new PyString("thread.error"); public static void start_new_thread(PyObject func, PyTuple args) { - Thread pt = new FunctionThread(func, args.getArray(), stack_size); + Thread pt = _newFunctionThread(func, args); PyObject currentThread = func.__findattr__("im_self"); if (currentThread != null) { PyObject isDaemon = currentThread.__findattr__("isDaemon"); @@ -44,8 +48,36 @@ } } pt.start(); - } + } + /** + * Initializes a {@link FunctionThread}, using the configured stack_size and + * registering the thread in the @link {@link #group} of threads spawned by + * the thread module. + * + * Also used from the threading.py module. + */ + public static FunctionThread _newFunctionThread(PyObject func, PyTuple args) { + return new FunctionThread(func, args.getArray(), stack_size, group); + } + + /** + * Interrupts all running threads spawned by the thread module. + * + * This works in conjuntion with:<ul> + * <li>{@link PyTableCode#call(org.python.core.PyFrame, PyObject)}: + * checks for the interrupted status of the current thread and raise + * a SystemRestart exception if a interruption is detected.</li> + * <li>{@link FunctionThread#run()}: exits the current thread when a + * SystemRestart exception is not caught.</li> + * + * Thus, it is possible that this doesn't make all running threads to stop, + * if SystemRestart exception is caught. + */ + public static void interruptAllThreads() { + group.interrupt(); + } + public static PyLock allocate_lock() { return new PyLock(); } @@ -81,4 +113,4 @@ throw Py.TypeError("stack_size() takes at most 1 argument (" + args.length + "given)"); } } -} \ No newline at end of file +} Modified: branches/asm/src/org/python/util/jython.java =================================================================== --- branches/asm/src/org/python/util/jython.java 2008-08-06 23:51:03 UTC (rev 5099) +++ branches/asm/src/org/python/util/jython.java 2008-08-07 02:27:08 UTC (rev 5100) @@ -15,12 +15,13 @@ import org.python.core.PyException; import org.python.core.PyFile; import org.python.core.PyModule; -import org.python.core.PyObject; import org.python.core.PyString; import org.python.core.PyStringMap; import org.python.core.PySystemState; import org.python.core.imp; import org.python.core.util.RelativeFile; +import org.python.modules._systemrestart; +import org.python.modules.thread.thread; public class jython { @@ -59,6 +60,8 @@ "JYTHONPATH: '" + java.io.File.pathSeparator + "'-separated list of directories prefixed to the default module\n" + " search path. The result is sys.path."; + private static boolean shouldRestart; + public static void runJar(String filename) { // TBD: this is kind of gross because a local called `zipfile' just // magically shows up in the module's globals. Either `zipfile' @@ -102,6 +105,13 @@ } public static void main(String[] args) { + do { + shouldRestart = false; + run(args); + } while (shouldRestart); + } + + public static void run(String[] args) { // Parse the command line options CommandLineOptions opts = new CommandLineOptions(); if (!opts.parse(args)) { @@ -120,22 +130,8 @@ opts.properties, opts.argv); // Now create an interpreter - InteractiveConsole interp = null; - try { - String interpClass = PySystemState.registry.getProperty( - "python.console", - "org.python.util.InteractiveConsole"); - interp = (InteractiveConsole) - Class.forName(interpClass).newInstance(); - } catch (Exception e) { - interp = new InteractiveConsole(); - } + InteractiveConsole interp = newInterpreter(); - //System.err.println("interp"); - PyModule mod = imp.addModule("__main__"); - interp.setLocals(mod.__dict__); - //System.err.println("imp"); - for (int i = 0; i < opts.warnoptions.size(); i++) { String wopt = (String) opts.warnoptions.elementAt(i); PySystemState.warnoptions.append(new PyString(wopt)); @@ -224,10 +220,20 @@ interp.execfile(file, opts.filename); } } catch(Throwable t) { - Py.printException(t); - if (!opts.interactive) { - interp.cleanup(); - System.exit(-1); + if (t instanceof PyException && + Py.matchException((PyException)t, _systemrestart.SystemRestart)) { + // Stop current threads... + thread.interruptAllThreads(); + // ..reset the state... + Py.setSystemState(new PySystemState()); + // ...and start again + shouldRestart = true; + } else { + Py.printException(t); + if (!opts.interactive) { + interp.cleanup(); + System.exit(-1); + } } } } @@ -273,8 +279,31 @@ System.exit(0); } } + + /** + * @return a new python interpreter, instantiating the right + * InteractiveConsole subclass for the configured <tt>python.console</tt> + */ + private static InteractiveConsole newInterpreter() { + InteractiveConsole interp = null; + try { + String interpClass = PySystemState.registry.getProperty( + "python.console", + "org.python.util.InteractiveConsole"); + interp = (InteractiveConsole) + Class.forName(interpClass).newInstance(); + } catch (Exception e) { + interp = new InteractiveConsole(); } + //System.err.println("interp"); + PyModule mod = imp.addModule("__main__"); + interp.setLocals(mod.__dict__); + //System.err.println("imp"); + return interp; + } +} + class CommandLineOptions { public String filename; This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |