From 2d8102c8f5790594c11df5a1bd46c540e4608cbd Mon Sep 17 00:00:00 2001 From: Andrew Kent Date: Fri, 17 Aug 2018 15:37:34 -0700 Subject: [PATCH 1/2] Register Datadog classloader as parallel capable --- .../trace/bootstrap/DatadogClassLoader.java | 14 +++++++ .../bootstrap/DatadogClassLoaderTest.groovy | 39 +++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 dd-java-agent/agent-bootstrap/src/test/groovy/datadog/trace/bootstrap/DatadogClassLoaderTest.groovy diff --git a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/DatadogClassLoader.java b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/DatadogClassLoader.java index 50ee58e0cc..abccdc0ddd 100644 --- a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/DatadogClassLoader.java +++ b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/DatadogClassLoader.java @@ -5,6 +5,10 @@ import java.net.URLClassLoader; /** Classloader used to run the core datadog agent. */ public class DatadogClassLoader extends URLClassLoader { + static { + ClassLoader.registerAsParallelCapable(); + } + // Calling java.lang.instrument.Instrumentation#appendToBootstrapClassLoaderSearch // adds a jar to the bootstrap class lookup, but not to the resource lookup. // As a workaround, we keep a reference to the bootstrap jar @@ -24,6 +28,12 @@ public class DatadogClassLoader extends URLClassLoader { bootstrapProxy = new BootstrapClassLoaderProxy(new URL[] {bootstrapJarLocation}, null); } + /** Public for testing only */ + @Override + public Object getClassLoadingLock(String className) { + return super.getClassLoadingLock(className); + } + @Override public URL getResource(String resourceName) { final URL bootstrapResource = bootstrapProxy.getResource(resourceName); @@ -45,6 +55,10 @@ public class DatadogClassLoader extends URLClassLoader { *

This class is thread safe. */ public static final class BootstrapClassLoaderProxy extends URLClassLoader { + static { + ClassLoader.registerAsParallelCapable(); + } + public BootstrapClassLoaderProxy(URL[] urls, ClassLoader parent) { super(urls, parent); } diff --git a/dd-java-agent/agent-bootstrap/src/test/groovy/datadog/trace/bootstrap/DatadogClassLoaderTest.groovy b/dd-java-agent/agent-bootstrap/src/test/groovy/datadog/trace/bootstrap/DatadogClassLoaderTest.groovy new file mode 100644 index 0000000000..8b679fa22b --- /dev/null +++ b/dd-java-agent/agent-bootstrap/src/test/groovy/datadog/trace/bootstrap/DatadogClassLoaderTest.groovy @@ -0,0 +1,39 @@ +package datadog.trace.bootstrap + +import spock.lang.Specification + +import java.util.concurrent.Phaser + +class DatadogClassLoaderTest extends Specification { + def "DD classloader does not lock classloading around instance" () { + setup: + def className1 = 'some/class/Name1' + def className2 = 'some/class/Name2' + final URL loc = getClass().getProtectionDomain().getCodeSource().getLocation() + final DatadogClassLoader ddLoader = new DatadogClassLoader(loc, loc, (ClassLoader) null) + final Phaser threadHoldLockPhase = new Phaser(2) + final Phaser acquireLockFromMainThreadPhase = new Phaser(2) + + when: + final Thread thread = new Thread() { + @Override + void run() { + synchronized (ddLoader.getClassLoadingLock(className1)) { + threadHoldLockPhase.arrive() + acquireLockFromMainThreadPhase.arriveAndAwaitAdvance() + } + } + } + thread.start() + + threadHoldLockPhase.arriveAndAwaitAdvance() + synchronized (ddLoader.getClassLoadingLock(className2)) { + acquireLockFromMainThreadPhase.arrive() + } + thread.join() + boolean applicationDidNotDeadlock = true + + then: + applicationDidNotDeadlock + } +} From e28ee5f9e8860be88221efee0f1768b4c656f402 Mon Sep 17 00:00:00 2001 From: Andrew Kent Date: Mon, 20 Aug 2018 11:10:08 -0700 Subject: [PATCH 2/2] Add timeout to datadog classloader test --- .../trace/bootstrap/DatadogClassLoader.java | 6 ----- .../bootstrap/DatadogClassLoaderTest.groovy | 22 ++++++++++++++----- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/DatadogClassLoader.java b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/DatadogClassLoader.java index abccdc0ddd..5a88289703 100644 --- a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/DatadogClassLoader.java +++ b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/DatadogClassLoader.java @@ -28,12 +28,6 @@ public class DatadogClassLoader extends URLClassLoader { bootstrapProxy = new BootstrapClassLoaderProxy(new URL[] {bootstrapJarLocation}, null); } - /** Public for testing only */ - @Override - public Object getClassLoadingLock(String className) { - return super.getClassLoadingLock(className); - } - @Override public URL getResource(String resourceName) { final URL bootstrapResource = bootstrapProxy.getResource(resourceName); diff --git a/dd-java-agent/agent-bootstrap/src/test/groovy/datadog/trace/bootstrap/DatadogClassLoaderTest.groovy b/dd-java-agent/agent-bootstrap/src/test/groovy/datadog/trace/bootstrap/DatadogClassLoaderTest.groovy index 8b679fa22b..842b9573fe 100644 --- a/dd-java-agent/agent-bootstrap/src/test/groovy/datadog/trace/bootstrap/DatadogClassLoaderTest.groovy +++ b/dd-java-agent/agent-bootstrap/src/test/groovy/datadog/trace/bootstrap/DatadogClassLoaderTest.groovy @@ -1,10 +1,13 @@ package datadog.trace.bootstrap import spock.lang.Specification +import spock.lang.Timeout import java.util.concurrent.Phaser +import java.util.concurrent.TimeUnit class DatadogClassLoaderTest extends Specification { + @Timeout(value = 60, unit = TimeUnit.SECONDS) def "DD classloader does not lock classloading around instance" () { setup: def className1 = 'some/class/Name1' @@ -15,7 +18,7 @@ class DatadogClassLoaderTest extends Specification { final Phaser acquireLockFromMainThreadPhase = new Phaser(2) when: - final Thread thread = new Thread() { + final Thread thread1 = new Thread() { @Override void run() { synchronized (ddLoader.getClassLoadingLock(className1)) { @@ -24,13 +27,20 @@ class DatadogClassLoaderTest extends Specification { } } } - thread.start() + thread1.start() - threadHoldLockPhase.arriveAndAwaitAdvance() - synchronized (ddLoader.getClassLoadingLock(className2)) { - acquireLockFromMainThreadPhase.arrive() + final Thread thread2 = new Thread() { + @Override + void run() { + threadHoldLockPhase.arriveAndAwaitAdvance() + synchronized (ddLoader.getClassLoadingLock(className2)) { + acquireLockFromMainThreadPhase.arrive() + } + } } - thread.join() + thread2.start() + thread1.join() + thread2.join() boolean applicationDidNotDeadlock = true then: