Commits

Jim Baker committed b443aef

To address #1327 - removed use of Guava's FinalizableReferenceQueue
and now does a passive cleanup of PySystemState-related data, so that
classloaders will hopefully GC under a range of setups. Also
de-registers the shutdown hook.

Comments (0)

Files changed (5)

Lib/test/test_jy_internals.py

 
 Dog().bark()
 """)
-            pi.cleanup();
             make_clean()
     
         # get to steady state first, then verify we don't create new proxies

Lib/test/test_weakref.py

     def extra_collect():
         """Kick Java's GC into gear"""
         gc.collect()
-        time.sleep(0.1)
+        time.sleep(0.2)
         gc.collect()
+        time.sleep(0.2)
         gc.collect()
 else:
     def extra_collect():

src/org/python/core/Py.java

         // memory error.  Fix for bug #1654484
         PyType.fromClass(OutOfMemoryError.class);
     }
-    public static PySystemState defaultSystemState;
+    public static volatile PySystemState defaultSystemState;
     // This is a hack to get initializations to work in proper order
     public static synchronized boolean initPython() {
         PySystemState.initialize();

src/org/python/core/PySystemState.java

 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
+import java.lang.ref.Reference;
+import java.lang.ref.ReferenceQueue;
 import java.lang.ref.WeakReference;
 import java.net.URL;
 import java.net.URLDecoder;
 import java.util.Properties;
 import java.util.Set;
 import java.util.StringTokenizer;
+import java.util.concurrent.ConcurrentMap;
 import java.util.jar.JarEntry;
 import java.util.jar.JarFile;
 
-import com.google.common.base.FinalizablePhantomReference;
-import com.google.common.base.FinalizableReferenceQueue;
+import com.google.common.collect.MapMaker;
 import org.jruby.ext.posix.util.Platform;
 import org.python.Version;
 import org.python.core.adapter.ClassicPyObjectAdapter;
 
     // Automatically close resources associated with a PySystemState when they get GCed
     private final PySystemStateCloser closer;
-    private static final FinalizableReferenceQueue systemStateQueue = new FinalizableReferenceQueue();
-
+    private static final ReferenceQueue systemStateQueue = new ReferenceQueue<PySystemState>();
+    private static final ConcurrentMap<WeakReference<PySystemState>, PySystemStateCloser> sysClosers = Generic.concurrentMap();
+//    static {
+//        startCleanupThread();
+//    }
 
     public PySystemState() {
         initialize();
         return closer.unregisterCloser(resourceCloser);
     }
 
-    public void shutdown() {
-        closer.shutdown();
+    public void cleanup() {
+        closer.cleanup();
     }
 
+    private static void startCleanupThread() {
+        Thread cleanupThread = new Thread(new Runnable() {
+            public void run() {
+                while (true) {
+                    try {
+                        Reference<PySystemStateCloser> ref = systemStateQueue.remove();
+                        PySystemStateCloser closer = sysClosers.get(ref);
+                        closer.cleanup();
+                        sysClosers.remove(ref);
+                    } catch (InterruptedException ex) {
+                        break;
+                    }
+                }
+            }
+        });
+        cleanupThread.setDaemon(true);
+        cleanupThread.start();
+    }
 
     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 volatile boolean isCleanup = false;
+        private final Thread shutdownHook;
 
         private PySystemStateCloser(PySystemState sys) {
-            this.sys = new FinalizablePhantomReference<PySystemState>(sys, systemStateQueue) {
-                public void finalizeReferent() {
-                    shutdown();
-                }
-            };
-            initShutdownCloser();
+            shutdownHook = initShutdownCloser();
+            WeakReference<PySystemState> ref = new WeakReference(sys, systemStateQueue);
+            sysClosers.put(ref, this);
+            cleanupOtherClosers();
+        }
+
+        private static void cleanupOtherClosers() {
+            Reference<PySystemStateCloser> ref;
+            while ((ref = systemStateQueue.poll()) != null) {
+                PySystemStateCloser closer = sysClosers.get(ref);
+                closer.cleanup();
+            }
         }
 
         private synchronized void registerThreadState(ThreadState[] threadLocal, ThreadState ts) {
-            if (!isShutdown) { // is this really necessary?
+            if (!isCleanup) {
                 threadLocal[0] = ts;
                 threadStateList.add(new WeakReference<ThreadState[]>(threadLocal));
             }
         }
 
         private synchronized void registerCloser(Callable closer) {
-            if (!isShutdown) {
+            if (!isCleanup) {
                 resourceClosers.add(closer);
             }
         }
             return resourceClosers.remove(closer);
         }
 
-        private synchronized void shutdown() {
-            if (isShutdown) {
+        private synchronized void cleanup() {
+            if (isCleanup) {
                 return;
             }
-            isShutdown = true;
+            isCleanup = true;
+
+            // close this thread so we can unload any associated classloaders in cycle with this instance
+            if (shutdownHook != null) {
+                Runtime.getRuntime().removeShutdownHook(shutdownHook);
+            }
 
             // clear out existing ThreadStates so that they can be GCed - this resolves ClassLoader issues
             for (WeakReference<ThreadState[]> ref : threadStateList) {
                 try {
                     callable.call();
                 } catch (Exception e) {
-                    // just continue
+                    // just continue, nothing we can do
                 }
             }
             resourceClosers.clear();
         }
 
-        // Python scripts expect that files are closed upon an orderly shutdown of the VM.
-        private void initShutdownCloser() {
+        // Python scripts expect that files are closed upon an orderly cleanup of the VM.
+        private Thread initShutdownCloser() {
             try {
-                Runtime.getRuntime().addShutdownHook(new ShutdownCloser());
+                Thread shutdownHook = new ShutdownCloser();
+                Runtime.getRuntime().addShutdownHook(shutdownHook);
+                return shutdownHook;
             } catch (SecurityException se) {
-                Py.writeDebug("PySystemState", "Can't register shutdown closer hook");
+                Py.writeDebug("PySystemState", "Can't register cleanup closer hook");
+                return null;
             }
         }
 

src/org/python/util/PythonInterpreter.java

 
     public void cleanup() {
         setSystemState();
-        Py.getSystemState().callExitFunc();
+        PySystemState sys = Py.getSystemState();
+        sys.callExitFunc();
         try {
-            Py.getSystemState().stdout.invoke("flush");
+            sys.stdout.invoke("flush");
         } catch (PyException pye) {
             // fall through
         }
         try {
-            Py.getSystemState().stderr.invoke("flush");
+            sys.stderr.invoke("flush");
         } catch (PyException pye) {
             // fall through
         }
+        sys.cleanup();
     }
 }