Skip to content

Commit 0444100

Browse files
committed
JythonScriptLanguage: clean interpreter
Uses PhantomReferences to clean up after the interpreter
1 parent 9e81c53 commit 0444100

File tree

1 file changed

+93
-1
lines changed

1 file changed

+93
-1
lines changed

src/main/java/org/scijava/plugins/scripting/jython/JythonScriptLanguage.java

Lines changed: 93 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,23 @@
3131

3232
package org.scijava.plugins.scripting.jython;
3333

34+
import java.lang.ref.PhantomReference;
35+
import java.lang.ref.ReferenceQueue;
36+
import java.util.ArrayList;
37+
import java.util.LinkedList;
38+
import java.util.List;
39+
3440
import javax.script.ScriptEngine;
3541

3642
import org.python.core.PyNone;
3743
import org.python.core.PyObject;
3844
import org.python.core.PyString;
45+
import org.python.util.PythonInterpreter;
46+
import org.scijava.plugin.Parameter;
3947
import org.scijava.plugin.Plugin;
4048
import org.scijava.script.AdaptedScriptLanguage;
4149
import org.scijava.script.ScriptLanguage;
50+
import org.scijava.thread.ThreadService;
4251

4352
/**
4453
* An adapter of the Jython interpreter to the SciJava scripting interface.
@@ -49,14 +58,66 @@
4958
@Plugin(type = ScriptLanguage.class, name = "Python")
5059
public class JythonScriptLanguage extends AdaptedScriptLanguage {
5160

61+
private final LinkedList<JythonEnginePhantomReference> phantomReferences =
62+
new LinkedList<JythonEnginePhantomReference>();
63+
private final ReferenceQueue<JythonScriptEngine> queue =
64+
new ReferenceQueue<JythonScriptEngine>();
65+
66+
@Parameter
67+
private ThreadService threadService;
68+
5269
public JythonScriptLanguage() {
5370
super("jython");
5471
}
5572

5673
@Override
5774
public ScriptEngine getScriptEngine() {
5875
// TODO: Consider adapting the wrapped ScriptEngineFactory's ScriptEngine.
59-
return new JythonScriptEngine();
76+
final JythonScriptEngine engine = new JythonScriptEngine();
77+
78+
synchronized (phantomReferences) {
79+
phantomReferences.add(new JythonEnginePhantomReference(engine, queue));
80+
81+
// If we added references to an empty queue, we need to start
82+
// a new polling thread
83+
if (phantomReferences.size() == 1) {
84+
threadService.run(new Runnable() {
85+
86+
@Override
87+
public void run() {
88+
boolean done = false;
89+
90+
// poll the queue
91+
while (!done) {
92+
try {
93+
Thread.sleep(100);
94+
95+
synchronized (phantomReferences) {
96+
JythonEnginePhantomReference ref =
97+
(JythonEnginePhantomReference) queue.poll();
98+
99+
// if we have a ref, clean it up
100+
if (ref != null) {
101+
ref.cleanup();
102+
phantomReferences.remove(ref);
103+
}
104+
105+
// Once we're done with our known phantom refs
106+
// we can shut down this thread.
107+
done = phantomReferences.size() == 0;
108+
}
109+
110+
}
111+
catch (final Exception ex) {
112+
// log exception, continue
113+
}
114+
}
115+
}
116+
});
117+
118+
}
119+
}
120+
return engine;
60121
}
61122

62123
@Override
@@ -74,4 +135,35 @@ public Object decode(final Object object) {
74135
return object;
75136
}
76137

138+
private static class JythonEnginePhantomReference extends
139+
PhantomReference<JythonScriptEngine>
140+
{
141+
142+
public PythonInterpreter interpreter;
143+
144+
public JythonEnginePhantomReference(JythonScriptEngine engine,
145+
ReferenceQueue<JythonScriptEngine> queue)
146+
{
147+
super(engine, queue);
148+
interpreter = engine.interpreter;
149+
}
150+
151+
public void cleanup() {
152+
final List<String> scriptLocals = new ArrayList<String>();
153+
PythonInterpreter interp = interpreter;
154+
if (interp == null) return;
155+
156+
final PyObject locals = interp.getLocals();
157+
for (final PyObject item : locals.__iter__().asIterable()) {
158+
final String localVar = item.toString();
159+
if (!localVar.contains("__name__") && !localVar.contains("__doc__")) {
160+
161+
scriptLocals.add(item.toString());
162+
}
163+
}
164+
for (final String string : scriptLocals) {
165+
interp.set(string, null);
166+
}
167+
}
168+
}
77169
}

0 commit comments

Comments
 (0)