[Jython-checkins] jython: Make RefReaperThread a Runnable and ensure it terminates to avoid ClassLoader

jim.baker jython-checkins at python.org
Sat Jun 28 03:28:17 CEST 2014


http://hg.python.org/jython/rev/4e538d0ebbd7
changeset:   7327:4e538d0ebbd7
user:        Indra Talip <indra.talip at gmail.com>
date:        Wed Jun 25 00:13:34 2014 -0600
summary:
  Make RefReaperThread a Runnable and ensure it terminates to avoid ClassLoader resource leaks

files:
  src/org/python/modules/_weakref/GlobalRef.java |  73 +++++++--
  1 files changed, 58 insertions(+), 15 deletions(-)


diff --git a/src/org/python/modules/_weakref/GlobalRef.java b/src/org/python/modules/_weakref/GlobalRef.java
--- a/src/org/python/modules/_weakref/GlobalRef.java
+++ b/src/org/python/modules/_weakref/GlobalRef.java
@@ -6,11 +6,14 @@
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.Callable;
 import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
 
 import org.python.core.Py;
 import org.python.core.PyList;
 import org.python.core.PyObject;
+import org.python.core.PySystemState;
 import org.python.util.Generic;
 
 public class GlobalRef extends WeakReference {
@@ -34,14 +37,11 @@
 
     private static ReferenceQueue referenceQueue = new ReferenceQueue();
 
-    private static RefReaperThread reaperThread;
+    private static Thread reaperThread;
+    private static ReentrantReadWriteLock reaperLock = new ReentrantReadWriteLock();
 
     private static ConcurrentMap<GlobalRef, GlobalRef> objects = Generic.concurrentMap();
 
-    static {
-        initReaperThread();
-    }
-
     public GlobalRef(PyObject object) {
         super(object, referenceQueue);
         hashCode = System.identityHashCode(object);
@@ -117,14 +117,36 @@
      * @return a new tracked GlobalRef
      */
     public static GlobalRef newInstance(PyObject object) {
-        GlobalRef ref = objects.get(new GlobalRef(object));
+        createReaperThreadIfAbsent();
+
+        GlobalRef newRef = new GlobalRef(object);
+        GlobalRef ref = objects.putIfAbsent(newRef, newRef);
         if (ref == null) {
-            ref = new GlobalRef(object);
-            objects.put(ref, ref);
+            ref = newRef;
         }
         return ref;
     }
 
+    private static void createReaperThreadIfAbsent() {
+        reaperLock.readLock().lock();
+        try {
+            if (reaperThread == null || !reaperThread.isAlive()) {
+                reaperLock.readLock().unlock();
+                reaperLock.writeLock().lock();
+                if (reaperThread == null || !reaperThread.isAlive()) {
+                    try {
+                        initReaperThread();
+                    } finally {
+                        reaperLock.readLock().lock();
+                        reaperLock.writeLock().unlock();
+                    }
+                }
+            }
+        } finally {
+            reaperLock.readLock().unlock();
+        }
+    }
+
     /**
      * Return the number of references to the specified PyObject.
      *
@@ -197,16 +219,18 @@
     }
 
     private static void initReaperThread() {
-        reaperThread = new RefReaperThread();
+        RefReaper reaper = new RefReaper();
+        PySystemState systemState = Py.getSystemState();
+        systemState.registerCloser(reaper);
+
+        reaperThread = new Thread(reaper, "weakref reaper");
         reaperThread.setDaemon(true);
         reaperThread.start();
     }
 
-    private static class RefReaperThread extends Thread {
-
-        RefReaperThread() {
-            super("weakref reaper");
-        }
+    private static class RefReaper implements Runnable, Callable<Void> {
+        private volatile boolean exit = false;
+        private Thread thread;
 
         public void collect() throws InterruptedException {
             GlobalRef gr = (GlobalRef)referenceQueue.remove();
@@ -216,13 +240,32 @@
         }
 
         public void run() {
+            // Store the actual reaper thread so that when PySystemState.cleanup()
+            // is called this thread can be interrupted and die.
+            this.thread = Thread.currentThread();
+
             while (true) {
                 try {
                     collect();
                 } catch (InterruptedException exc) {
-                    // ok
+                    // Is cleanup time so break out and die.
+                    if (exit) {
+                        break;
+                    }
                 }
             }
         }
+
+        @Override
+        public Void call() throws Exception {
+            this.exit = true;
+
+            if (thread != null && thread.isAlive()) {
+                this.thread.interrupt();
+                this.thread = null;
+            }
+
+            return null;
+        }
     }
 }

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


More information about the Jython-checkins mailing list