From 1438b79378ccdf9d63e56db2bc8b8b02302bda2e Mon Sep 17 00:00:00 2001 From: Ethan Atkins Date: Tue, 3 Dec 2019 18:43:18 -0800 Subject: [PATCH] Make ZombieClassLoader thread safe The previous implementation of ZombieClassLoader was not thread safe. This caused problems because it is possible for the ManagedClassLoader in test to leak into the coursier thread pool if the test uses bouncy castle apis. Unfortunately, these apis seem to in some cases assign static variables using the Thread context class loader. Because the bouncycastle apis are implemented by the jdk, they are found in the system classloader and thus the static references leak out of the test context. I had a local repro of https://github.com/sbt/sbt/issues/5249 that is fixed by this change. --- .../main/java/sbt/internal/ManagedClassLoader.java | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/main/src/main/java/sbt/internal/ManagedClassLoader.java b/main/src/main/java/sbt/internal/ManagedClassLoader.java index 46d160b0d..a1701a3cb 100644 --- a/main/src/main/java/sbt/internal/ManagedClassLoader.java +++ b/main/src/main/java/sbt/internal/ManagedClassLoader.java @@ -39,13 +39,16 @@ abstract class ManagedClassLoader extends URLClassLoader implements NativeLoader private final URL[] urls; ZombieClassLoader(URL[] urls) { - super(urls, ManagedClassLoader.this); + super(urls, ManagedClassLoader.this.getParent()); this.urls = urls; } Class lookupClass(final String name) throws ClassNotFoundException { try { - return findClass(name); + synchronized (getClassLoadingLock(name)) { + final Class previous = findLoadedClass(name); + return previous != null ? previous : findClass(name); + } } catch (final ClassNotFoundException e) { final StringBuilder builder = new StringBuilder(); for (final URL u : urls) { @@ -89,7 +92,12 @@ abstract class ManagedClassLoader extends URLClassLoader implements NativeLoader @Override protected Class findClass(String name) throws ClassNotFoundException { - return closed.get() ? getZombieLoader(name).lookupClass(name) : super.findClass(name); + try { + return super.findClass(name); + } catch (final NoClassDefFoundError | ClassNotFoundException e) { + if (closed.get()) return getZombieLoader(name).lookupClass(name); + else throw e; + } } @Override