Skip to content

Commit

Permalink
Remove Var.dvals at the top of the call stack [fixes #4]
Browse files Browse the repository at this point in the history
If any code causes Var.dvals to be set outside of a push/pop pair, and a
later pop never occurs to remove it, then it will cause the thread that
called .invoke to hold a reference to Var.dvals, which will cause all of
the classes loaded in the pod to be retained.

This removes dvals, but only if we are the outer .invoke on the current
thread (in case code invoked reaches out of the shim then back in).
  • Loading branch information
tobias committed Oct 23, 2015
1 parent 63d8286 commit 4ba67f7
Showing 1 changed file with 30 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,50 @@
import clojure.lang.Var;
import org.projectodd.shimdandy.ClojureRuntimeShim;

import java.lang.reflect.Field;
import java.util.concurrent.atomic.AtomicLong;

/**
*/
public class ClojureRuntimeShimImpl extends ClojureRuntimeShim {
public void init() {
ClassLoader origLoader = preInvoke();
Exception ex = null;
try {
Field dvalField = Var.class.getDeclaredField("dvals");
dvalField.setAccessible(true);
this.dvals = (ThreadLocal)dvalField.get(null);

this.require = RT.var("clojure.core", "require");
this.resolve = RT.var("clojure.core", "resolve");
clojure.lang.Compiler.LOADER.bindRoot(this.classLoader);
} catch (IllegalAccessException e) {
ex = e;
} catch (NoSuchFieldException e) {
ex = e;
} finally {
postInvoke(origLoader);
}

if (ex != null) {
throw new RuntimeException("Failed to access Var.dvals", ex);
}
}

protected ClassLoader preInvoke() {
ClassLoader originalClassloader = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader( this.classLoader );
final ClassLoader originalClassloader = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(this.classLoader);
this.callDepth.get().getAndIncrement();

return originalClassloader;
}

protected void postInvoke(ClassLoader loader) {
Thread.currentThread().setContextClassLoader( loader );
if (this.callDepth.get().decrementAndGet() == 0) {
this.dvals.remove();
this.callDepth.remove();
}
Thread.currentThread().setContextClassLoader(loader);
}

protected Var var(String namespacedFunction) {
Expand Down Expand Up @@ -579,4 +600,10 @@ public Object invoke(Object fn, Object arg1, Object arg2,

private Var require;
private Var resolve;
private ThreadLocal dvals;
private final ThreadLocal<AtomicLong> callDepth = new ThreadLocal<AtomicLong>() {
protected AtomicLong initialValue() {
return new AtomicLong(0);
}
};
}

0 comments on commit 4ba67f7

Please sign in to comment.