From: <zy...@us...> - 2010-08-28 03:52:49
|
Revision: 7109 http://jython.svn.sourceforge.net/jython/?rev=7109&view=rev Author: zyasoft Date: 2010-08-28 03:52:43 +0000 (Sat, 28 Aug 2010) Log Message: ----------- Use Guava's FinalizableReferenceQueue and a new PySystemStateCloser class to manage the closing of those resources that prevent classloader GC (because they have hard refs to classes) after a PySystemState itself is GCed. This refactoring also moved the closing of user-unclosed files associated with a PyFile to the holding PySystemState, instead of having to wait for JVM shutdown. Now the JVM shutdown hook does this closing of resources through the PySystemStateCloser. Modified Paths: -------------- trunk/jython/src/org/python/core/PyFile.java trunk/jython/src/org/python/core/PySystemState.java trunk/jython/src/org/python/core/PyType.java trunk/jython/src/org/python/core/ThreadStateMapping.java Modified: trunk/jython/src/org/python/core/PyFile.java =================================================================== --- trunk/jython/src/org/python/core/PyFile.java 2010-08-27 13:20:44 UTC (rev 7108) +++ trunk/jython/src/org/python/core/PyFile.java 2010-08-28 03:52:43 UTC (rev 7109) @@ -6,7 +6,7 @@ import java.io.InputStream; import java.io.OutputStream; -import java.util.LinkedList; +import java.util.concurrent.Callable; import org.python.core.io.BinaryIOWrapper; import org.python.core.io.BufferedIOBase; @@ -78,13 +78,6 @@ * shutdown */ private Closer closer; - /** All PyFiles' closers */ - private static LinkedList<Closer> closers = new LinkedList<Closer>(); - - static { - initCloser(); - } - public PyFile() {} public PyFile(PyType subType) { @@ -155,7 +148,7 @@ String mode = ap.getString(1, "r"); int bufsize = ap.getInt(2, -1); file___init__(new FileIO(name.toString(), parseMode(mode)), name, mode, bufsize); - closer = new Closer(file); + closer = new Closer(file, Py.getSystemState()); } private void file___init__(RawIOBase raw, String name, String mode, int bufsize) { @@ -569,16 +562,9 @@ } } - private static void initCloser() { - try { - Runtime.getRuntime().addShutdownHook(new PyFileCloser()); - } catch (SecurityException se) { - Py.writeDebug("PyFile", "Can't register file closer hook"); - } - } - + /** - * A mechanism to make sure PyFiles are closed on exit. On creation Closer adds itself + * XXX update docs - A mechanism to make sure PyFiles are closed on exit. On creation Closer adds itself * to a list of Closers that will be run by PyFileCloser on JVM shutdown. When a * PyFile's close or finalize methods are called, PyFile calls its Closer.close which * clears Closer out of the shutdown queue. @@ -588,55 +574,39 @@ * be called during shutdown, so we can't use it. It's vital that this Closer has no * reference to the PyFile it's closing so the PyFile remains garbage collectable. */ - private static class Closer { + private static class Closer implements Callable { - /** The underlying file */ - private TextIOBase file; + /** + * The underlying file + */ + private final TextIOBase file; + private PySystemState sys; - public Closer(TextIOBase file) { + public Closer(TextIOBase file, PySystemState sys) { this.file = file; - // Add ourselves to the queue of Closers to be run on shutdown - synchronized (closers) { - closers.add(this); - } + this.sys = sys; + sys.registerCloser(this); } + // For closing directly + public void close() { - synchronized (closers) { - if (!closers.remove(this)) { - return; - } + if (sys.unregisterCloser(this)) { + file.close(); } - doClose(); + sys = null; } - public void doClose() { + // For closing as part of a shutdown process + + public Object call() { file.close(); + sys = null; + return null; } + } - private static class PyFileCloser extends Thread { - public PyFileCloser() { - super("Jython Shutdown File Closer"); - } - @Override - public void run() { - if (closers == null) { - // closers can be null in some strange cases - return; - } - synchronized (closers) { - while (closers.size() > 0) { - try { - closers.removeFirst().doClose(); - } catch (PyException e) { - // continue - } - } - } - } - } - } Modified: trunk/jython/src/org/python/core/PySystemState.java =================================================================== --- trunk/jython/src/org/python/core/PySystemState.java 2010-08-27 13:20:44 UTC (rev 7108) +++ trunk/jython/src/org/python/core/PySystemState.java 2010-08-28 03:52:43 UTC (rev 7109) @@ -7,18 +7,25 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.lang.ref.WeakReference; import java.net.URL; import java.net.URLDecoder; import java.nio.charset.Charset; import java.nio.charset.UnsupportedCharsetException; import java.security.AccessControlException; +import java.util.concurrent.Callable; +import java.util.ArrayList; +import java.util.LinkedHashSet; import java.util.Map; import java.util.Map.Entry; import java.util.Properties; +import java.util.Set; import java.util.StringTokenizer; import java.util.jar.JarEntry; import java.util.jar.JarFile; +import com.google.common.base.FinalizablePhantomReference; +import com.google.common.base.FinalizableReferenceQueue; import org.jruby.ext.posix.util.Platform; import org.python.Version; import org.python.core.adapter.ClassicPyObjectAdapter; @@ -144,8 +151,14 @@ /** true when a SystemRestart is triggered. */ public boolean _systemRestart = false; + // Automatically close resources associated with a PySystemState when they get GCed + private final PySystemStateCloser closer; + private static final FinalizableReferenceQueue systemStateQueue = new FinalizableReferenceQueue(); + + public PySystemState() { initialize(); + closer = new PySystemStateCloser(this); modules = new PyStringMap(); argv = (PyList)defaultArgv.repeat(1); @@ -1225,8 +1238,118 @@ throw Py.ValueError("call stack is not deep enough"); return f; } + + public void registerThreadState(ThreadState[] threadLocal, ThreadState ts) { + closer.registerThreadState(threadLocal, ts); + } + + public void registerCloser(Callable resourceCloser) { + closer.registerCloser(resourceCloser); + } + + public synchronized boolean unregisterCloser(Callable resourceCloser) { + return closer.unregisterCloser(resourceCloser); + } + + public void shutdown() { + closer.shutdown(); + } + + + private static class PySystemStateCloser { + + private final ArrayList<WeakReference<ThreadState[]>> threadStateList = new ArrayList<WeakReference<ThreadState[]>>(); + private final Set<Callable> resourceClosers = new LinkedHashSet<Callable>(); + private final FinalizablePhantomReference<PySystemState> sys; + private volatile boolean isShutdown = false; + + private PySystemStateCloser(PySystemState sys) { + this.sys = new FinalizablePhantomReference<PySystemState>(sys, systemStateQueue) { + public void finalizeReferent() { + shutdown(); + } + }; + initShutdownCloser(); + } + + private synchronized void registerThreadState(ThreadState[] threadLocal, ThreadState ts) { + if (!isShutdown) { // is this really necessary? + threadLocal[0] = ts; + threadStateList.add(new WeakReference<ThreadState[]>(threadLocal)); + } + } + + private synchronized void registerCloser(Callable closer) { + if (!isShutdown) { + resourceClosers.add(closer); + } + } + + private synchronized boolean unregisterCloser(Callable closer) { + return resourceClosers.remove(closer); + } + + private synchronized void shutdown() { + if (isShutdown) { + return; + } + isShutdown = true; + + // clear out existing ThreadStates so that they can be GCed - this resolves ClassLoader issues + for (WeakReference<ThreadState[]> ref : threadStateList) { + ThreadState[] o = ref.get(); + if (o != null) { + o[0] = null; + } + } + threadStateList.clear(); + + for (Callable callable : resourceClosers) { + try { + callable.call(); + } catch (Exception e) { + // just continue + } + } + resourceClosers.clear(); + } + + // Python scripts expect that files are closed upon an orderly shutdown of the VM. + private void initShutdownCloser() { + try { + Runtime.getRuntime().addShutdownHook(new ShutdownCloser()); + } catch (SecurityException se) { + Py.writeDebug("PySystemState", "Can't register shutdown closer hook"); + } + } + + private class ShutdownCloser extends Thread { + + private ShutdownCloser() { + super("Jython Shutdown Closer"); + } + + @Override + public synchronized void run() { + if (resourceClosers == null) { + // resourceClosers can be null in some strange cases + return; + } + for (Callable callable : resourceClosers) { + try { + callable.call(); // side effect of being removed from this set + } catch (Exception e) { + // continue - nothing we can do now! + } + } + resourceClosers.clear(); + } + } + + } } + class PySystemStateFunctions extends PyBuiltinFunctionSet { PySystemStateFunctions(String name, int index, int minargs, int maxargs) { Modified: trunk/jython/src/org/python/core/PyType.java =================================================================== --- trunk/jython/src/org/python/core/PyType.java 2010-08-27 13:20:44 UTC (rev 7108) +++ trunk/jython/src/org/python/core/PyType.java 2010-08-28 03:52:43 UTC (rev 7109) @@ -91,7 +91,7 @@ /** The number of __slots__ defined. */ private int numSlots; - private ReferenceQueue<PyType> subclasses_refq = new ReferenceQueue<PyType>(); + private transient ReferenceQueue<PyType> subclasses_refq = new ReferenceQueue<PyType>(); private Set<WeakReference<PyType>> subclasses = Generic.set(); /** Global mro cache. */ Modified: trunk/jython/src/org/python/core/ThreadStateMapping.java =================================================================== --- trunk/jython/src/org/python/core/ThreadStateMapping.java 2010-08-27 13:20:44 UTC (rev 7108) +++ trunk/jython/src/org/python/core/ThreadStateMapping.java 2010-08-28 03:52:43 UTC (rev 7109) @@ -1,26 +1,41 @@ package org.python.core; class ThreadStateMapping { - private static final ThreadLocal<ThreadState> cachedThreadState = new ThreadLocal<ThreadState>(); + private static final ThreadLocal<ThreadState[]> cachedThreadState = + new ThreadLocal<ThreadState[]>() { + @Override + protected ThreadState[] initialValue() { + return new ThreadState[1]; + } + }; public ThreadState getThreadState(PySystemState newSystemState) { - ThreadState ts = cachedThreadState.get(); + + // usual double checked locking pattern + ThreadState ts = cachedThreadState.get()[0]; + if (ts != null) { return ts; } + synchronized(this) { + ThreadState[] threadLocal = cachedThreadState.get(); + if(threadLocal[0] != null) + return (ThreadState)threadLocal[0]; - Thread t = Thread.currentThread(); - if (newSystemState == null) { - Py.writeDebug("threadstate", "no current system state"); - if (Py.defaultSystemState == null) { - PySystemState.initialize(); + Thread t = Thread.currentThread(); + if (newSystemState == null) { + Py.writeDebug("threadstate", "no current system state"); + if (Py.defaultSystemState == null) { + PySystemState.initialize(); + } + newSystemState = Py.defaultSystemState; } - newSystemState = Py.defaultSystemState; + + ts = new ThreadState(t, newSystemState); + + newSystemState.registerThreadState(threadLocal, ts); + return ts; } - - ts = new ThreadState(t, newSystemState); - cachedThreadState.set(ts); - return ts; } } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |