4735126: (cl) ClassLoader.loadClass locks all instances in chain when delegating
Summary: Added support for parallel-capable class loaders
Reviewed-by: alanb
--- a/jdk/make/java/java/mapfile-vers Mon Apr 06 11:29:03 2009 +0100
+++ b/jdk/make/java/java/mapfile-vers Mon Apr 06 18:46:20 2009 -0700
@@ -1,5 +1,5 @@
#
-# Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved.
+# Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
@@ -135,7 +135,8 @@
Java_java_lang_ClassLoader_00024NativeLibrary_find;
Java_java_lang_ClassLoader_00024NativeLibrary_load;
Java_java_lang_ClassLoader_00024NativeLibrary_unload;
- Java_java_lang_ClassLoader_registerNatives;
+ Java_java_lang_ClassLoader_getCaller;
+ Java_java_lang_ClassLoader_registerNatives;
Java_java_lang_Compiler_registerNatives;
Java_java_lang_Double_longBitsToDouble;
Java_java_lang_Double_doubleToRawLongBits;
--- a/jdk/src/share/classes/java/lang/Class.java Mon Apr 06 11:29:03 2009 +0100
+++ b/jdk/src/share/classes/java/lang/Class.java Mon Apr 06 18:46:20 2009 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright 1994-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1994-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -2846,14 +2846,14 @@
if (loader == null)
return desiredAssertionStatus0(this);
- synchronized(loader) {
- // If the classloader has been initialized with
- // the assertion directives, ask it. Otherwise,
- // ask the VM.
- return (loader.classAssertionStatus == null ?
- desiredAssertionStatus0(this) :
- loader.desiredAssertionStatus(getName()));
+ // If the classloader has been initialized with the assertion
+ // directives, ask it. Otherwise, ask the VM.
+ synchronized(loader.assertionLock) {
+ if (loader.classAssertionStatus != null) {
+ return loader.desiredAssertionStatus(getName());
+ }
}
+ return desiredAssertionStatus0(this);
}
// Retrieves the desired assertion status of this class from the VM
--- a/jdk/src/share/classes/java/lang/ClassLoader.java Mon Apr 06 11:29:03 2009 +0100
+++ b/jdk/src/share/classes/java/lang/ClassLoader.java Mon Apr 06 18:46:20 2009 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright 1994-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1994-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -40,14 +40,17 @@
import java.security.PrivilegedExceptionAction;
import java.security.ProtectionDomain;
import java.security.cert.Certificate;
+import java.util.Collections;
import java.util.Enumeration;
-import java.util.Hashtable;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import java.util.Stack;
import java.util.Map;
import java.util.Vector;
+import java.util.Hashtable;
+import java.util.WeakHashMap;
+import java.util.concurrent.ConcurrentHashMap;
import sun.misc.ClassFileTransformer;
import sun.misc.CompoundEnumeration;
import sun.misc.Resource;
@@ -91,6 +94,17 @@
* called the "bootstrap class loader", does not itself have a parent but may
* serve as the parent of a <tt>ClassLoader</tt> instance.
*
+ * <p> Class loaders that support concurrent loading of classes are known as
+ * <em>parallel capable</em> class loaders and are required to register
+ * themselves at their class initialization time by invoking the
+ * {@link
+ * #registerAsParallelCapable <tt>ClassLoader.registerAsParallelCapable</tt>}
+ * method. In environments in which the delegation model is not strictly
+ * hierarchical, class loaders need to be parallel capable, otherise class
+ * loading can lead to deadlocks because the loader lock is held for the
+ * duration of the class loading process (see {@link #loadClass
+ * <tt>loadClass</tt>} methods).
+ *
* <p> Normally, the Java virtual machine loads classes from the local file
* system in a platform-dependent manner. For example, on UNIX systems, the
* virtual machine loads classes from the directory defined by the
@@ -160,31 +174,51 @@
public abstract class ClassLoader {
private static native void registerNatives();
+
+ // Set of classes which are registered as parallel capable class loaders
+ private static final Set<Class<? extends ClassLoader>> parallelLoaders
+ = Collections.newSetFromMap(Collections.synchronizedMap
+ (new WeakHashMap<Class<? extends ClassLoader>, Boolean>()));
+
static {
registerNatives();
+ parallelLoaders.add(ClassLoader.class);
}
// If initialization succeed this is set to true and security checks will
// succeed. Otherwise the object is not initialized and the object is
// useless.
- private boolean initialized = false;
+ private final boolean initialized;
// The parent class loader for delegation
- private ClassLoader parent;
+ // Note: VM hardcoded the offset of this field, thus all new fields
+ // must be added *after* it.
+ private final ClassLoader parent;
+
+ // Maps class name to the corresponding lock object when the current
+ // class loader is parallel capable.
+ // Note: VM also uses this field to decide if the current class loader
+ // is parallel capable and the appropriate lock object for class loading.
+ private final ConcurrentHashMap<String, Object> parallelLockMap;
// Hashtable that maps packages to certs
- private Hashtable<String, Certificate[]> package2certs
- = new Hashtable<String, Certificate[]>(11);
+ private final Map <String, Certificate[]> package2certs;
// Shared among all packages with unsigned classes
- Certificate[] nocerts;
+ private static final Certificate[] nocerts = new Certificate[0];
+
+ // The classes loaded by this class loader. The only purpose of this table
+ // is to keep the classes from being GC'ed until the loader is GC'ed.
+ private final Vector<Class<?>> classes = new Vector<Class<?>>();
- // The classes loaded by this class loader. The only purpose of this table
- // is to keep the classes from being GC'ed until the loader is GC'ed.
- private Vector<Class<?>> classes = new Vector<Class<?>>();
+ // The "default" domain. Set as the default ProtectionDomain on newly
+ // created classes.
+ private final ProtectionDomain defaultDomain =
+ new ProtectionDomain(new CodeSource(null, (Certificate[]) null),
+ null, this, null);
// The initiating protection domains for all classes loaded by this loader
- private Set<ProtectionDomain> domains = new HashSet<ProtectionDomain>();
+ private final Set<ProtectionDomain> domains;
// Invoked by the VM to record every loaded class with this loader.
void addClass(Class c) {
@@ -193,7 +227,9 @@
// The packages defined in this class loader. Each package name is mapped
// to its corresponding Package object.
- private HashMap<String, Package> packages = new HashMap<String, Package>();
+ // @GuardedBy("itself")
+ private final HashMap<String, Package> packages =
+ new HashMap<String, Package>();
/**
* Creates a new class loader using the specified parent class loader for
@@ -220,6 +256,19 @@
security.checkCreateClassLoader();
}
this.parent = parent;
+ if (parallelLoaders.contains(this.getClass())) {
+ parallelLockMap = new ConcurrentHashMap<String, Object>();
+ package2certs = new ConcurrentHashMap<String, Certificate[]>();
+ domains =
+ Collections.synchronizedSet(new HashSet<ProtectionDomain>());
+ assertionLock = new Object();
+ } else {
+ // no finer-grained lock; lock on the classloader instance
+ parallelLockMap = null;
+ package2certs = new Hashtable<String, Certificate[]>();
+ domains = new HashSet<ProtectionDomain>();
+ assertionLock = this;
+ }
initialized = true;
}
@@ -244,10 +293,22 @@
security.checkCreateClassLoader();
}
this.parent = getSystemClassLoader();
+ if (parallelLoaders.contains(this.getClass())) {
+ parallelLockMap = new ConcurrentHashMap<String, Object>();
+ package2certs = new ConcurrentHashMap<String, Certificate[]>();
+ domains =
+ Collections.synchronizedSet(new HashSet<ProtectionDomain>());
+ assertionLock = new Object();
+ } else {
+ // no finer-grained lock; lock on the classloader instance
+ parallelLockMap = null;
+ package2certs = new Hashtable<String, Certificate[]>();
+ domains = new HashSet<ProtectionDomain>();
+ assertionLock = this;
+ }
initialized = true;
}
-
// -- Class --
/**
@@ -296,6 +357,10 @@
* <p> Subclasses of <tt>ClassLoader</tt> are encouraged to override {@link
* #findClass(String)}, rather than this method. </p>
*
+ * <p> Unless overridden, this method synchronizes on the result of
+ * {@link #getClassLoadingLock <tt>getClassLoadingLock</tt>} method
+ * during the entire class loading process.
+ *
* @param name
* The <a href="#name">binary name</a> of the class
*
@@ -307,37 +372,80 @@
* @throws ClassNotFoundException
* If the class could not be found
*/
- protected synchronized Class<?> loadClass(String name, boolean resolve)
+ protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
- // First, check if the class has already been loaded
- Class c = findLoadedClass(name);
- if (c == null) {
- try {
- if (parent != null) {
- c = parent.loadClass(name, false);
- } else {
- c = findBootstrapClass0(name);
+ synchronized (getClassLoadingLock(name)) {
+ // First, check if the class has already been loaded
+ Class c = findLoadedClass(name);
+ if (c == null) {
+ try {
+ if (parent != null) {
+ c = parent.loadClass(name, false);
+ } else {
+ c = findBootstrapClass0(name);
+ }
+ } catch (ClassNotFoundException e) {
+ // If still not found, then invoke findClass in order
+ // to find the class.
+ c = findClass(name);
}
- } catch (ClassNotFoundException e) {
- // If still not found, then invoke findClass in order
- // to find the class.
- c = findClass(name);
+ }
+ if (resolve) {
+ resolveClass(c);
+ }
+ return c;
+ }
+ }
+
+ /**
+ * Returns the lock object for class loading operations.
+ * For backward compatibility, the default implementation of this method
+ * behaves as follows. If this ClassLoader object is registered as
+ * parallel capable, the method returns a dedicated object associated
+ * with the specified class name. Otherwise, the method returns this
+ * ClassLoader object. </p>
+ *
+ * @param className
+ * The name of the to-be-loaded class
+ *
+ * @return the lock for class loading operations
+ *
+ * @throws NullPointerException
+ * If registered as parallel capable and <tt>className</tt> is null
+ *
+ * @see #loadClass(String, boolean)
+ *
+ * @since 1.7
+ */
+ protected Object getClassLoadingLock(String className) {
+ Object lock = this;
+ if (parallelLockMap != null) {
+ Object newLock = new Object();
+ lock = parallelLockMap.putIfAbsent(className, newLock);
+ if (lock == null) {
+ lock = newLock;
}
}
- if (resolve) {
- resolveClass(c);
- }
- return c;
+ return lock;
}
// This method is invoked by the virtual machine to load a class.
- private synchronized Class loadClassInternal(String name)
+ private Class loadClassInternal(String name)
throws ClassNotFoundException
{
- return loadClass(name);
+ // For backward compatibility, explicitly lock on 'this' when
+ // the current class loader is not parallel capable.
+ if (parallelLockMap == null) {
+ synchronized (this) {
+ return loadClass(name);
+ }
+ } else {
+ return loadClass(name);
+ }
}
+ // Invoked by the VM after loading class with this loader.
private void checkPackageAccess(Class cls, ProtectionDomain pd) {
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
@@ -486,31 +594,32 @@
/* Determine protection domain, and check that:
- not define java.* class,
- - signer of this class matches signers for the rest of the classes in package.
+ - signer of this class matches signers for the rest of the classes in
+ package.
*/
private ProtectionDomain preDefineClass(String name,
- ProtectionDomain protectionDomain)
+ ProtectionDomain pd)
{
if (!checkName(name))
throw new NoClassDefFoundError("IllegalName: " + name);
if ((name != null) && name.startsWith("java.")) {
- throw new SecurityException("Prohibited package name: " +
- name.substring(0, name.lastIndexOf('.')));
+ throw new SecurityException
+ ("Prohibited package name: " +
+ name.substring(0, name.lastIndexOf('.')));
}
- if (protectionDomain == null) {
- protectionDomain = getDefaultDomain();
+ if (pd == null) {
+ pd = defaultDomain;
}
- if (name != null)
- checkCerts(name, protectionDomain.getCodeSource());
+ if (name != null) checkCerts(name, pd.getCodeSource());
- return protectionDomain;
+ return pd;
}
- private String defineClassSourceLocation(ProtectionDomain protectionDomain)
+ private String defineClassSourceLocation(ProtectionDomain pd)
{
- CodeSource cs = protectionDomain.getCodeSource();
+ CodeSource cs = pd.getCodeSource();
String source = null;
if (cs != null && cs.getLocation() != null) {
source = cs.getLocation().toString();
@@ -519,14 +628,15 @@
}
private Class defineTransformedClass(String name, byte[] b, int off, int len,
- ProtectionDomain protectionDomain,
+ ProtectionDomain pd,
ClassFormatError cfe, String source)
throws ClassFormatError
{
// Class format error - try to transform the bytecode and
// define the class again
//
- ClassFileTransformer[] transformers = ClassFileTransformer.getTransformers();
+ ClassFileTransformer[] transformers =
+ ClassFileTransformer.getTransformers();
Class c = null;
if (transformers != null) {
@@ -535,7 +645,7 @@
// Transform byte code using transformer
byte[] tb = transformer.transform(b, off, len);
c = defineClass1(name, tb, 0, tb.length,
- protectionDomain, source);
+ pd, source);
break;
} catch (ClassFormatError cfe2) {
// If ClassFormatError occurs, try next transformer
@@ -552,11 +662,10 @@
return c;
}
- private void postDefineClass(Class c, ProtectionDomain protectionDomain)
+ private void postDefineClass(Class c, ProtectionDomain pd)
{
- if (protectionDomain.getCodeSource() != null) {
- Certificate certs[] =
- protectionDomain.getCodeSource().getCertificates();
+ if (pd.getCodeSource() != null) {
+ Certificate certs[] = pd.getCodeSource().getCertificates();
if (certs != null)
setSigners(c, certs);
}
@@ -641,7 +750,8 @@
try {
c = defineClass1(name, b, off, len, protectionDomain, source);
} catch (ClassFormatError cfe) {
- c = defineTransformedClass(name, b, off, len, protectionDomain, cfe, source);
+ c = defineTransformedClass(name, b, off, len, protectionDomain, cfe,
+ source);
}
postDefineClass(c, protectionDomain);
@@ -656,10 +766,10 @@
* specified in the documentation for {@link #defineClass(String, byte[],
* int, int)}. Before the class can be used it must be resolved.
*
- * <p>The rules about the first class defined in a package determining the set of
- * certificates for the package, and the restrictions on class names are identical
- * to those specified in the documentation for {@link #defineClass(String, byte[],
- * int, int, ProtectionDomain)}.
+ * <p>The rules about the first class defined in a package determining the
+ * set of certificates for the package, and the restrictions on class names
+ * are identical to those specified in the documentation for {@link
+ * #defineClass(String, byte[], int, int, ProtectionDomain)}.
*
* <p> An invocation of this method of the form
* <i>cl</i><tt>.defineClass(</tt><i>name</i><tt>,</tt>
@@ -668,12 +778,13 @@
*
* <blockquote><tt>
* ...<br>
- * byte[] temp = new byte[</tt><i>bBuffer</i><tt>.{@link java.nio.ByteBuffer#remaining
- * remaining}()];<br>
+ * byte[] temp = new byte[</tt><i>bBuffer</i><tt>.{@link
+ * java.nio.ByteBuffer#remaining remaining}()];<br>
* </tt><i>bBuffer</i><tt>.{@link java.nio.ByteBuffer#get(byte[])
* get}(temp);<br>
* return {@link #defineClass(String, byte[], int, int, ProtectionDomain)
- * </tt><i>cl</i><tt>.defineClass}(</tt><i>name</i><tt>, temp, 0, temp.length, </tt><i>pd</i><tt>);<br>
+ * </tt><i>cl</i><tt>.defineClass}(</tt><i>name</i><tt>, temp, 0,
+ * temp.length, </tt><i>pd</i><tt>);<br>
* </tt></blockquote>
*
* @param name
@@ -682,9 +793,9 @@
*
* @param b
* The bytes that make up the class data. The bytes from positions
- * <tt>b.position()</tt> through <tt>b.position() + b.limit() -1 </tt>
- * should have the format of a valid class file as defined by the <a
- * href="http://java.sun.com/docs/books/vmspec/">Java Virtual
+ * <tt>b.position()</tt> through <tt>b.position() + b.limit() -1
+ * </tt> should have the format of a valid class file as defined by
+ * the <a href="http://java.sun.com/docs/books/vmspec/">Java Virtual
* Machine Specification</a>.
*
* @param protectionDomain
@@ -738,11 +849,13 @@
String source = defineClassSourceLocation(protectionDomain);
try {
- c = defineClass2(name, b, b.position(), len, protectionDomain, source);
+ c = defineClass2(name, b, b.position(), len, protectionDomain,
+ source);
} catch (ClassFormatError cfe) {
byte[] tb = new byte[len];
b.get(tb); // get bytes out of byte buffer.
- c = defineTransformedClass(name, tb, 0, len, protectionDomain, cfe, source);
+ c = defineTransformedClass(name, tb, 0, len, protectionDomain, cfe,
+ source);
}
postDefineClass(c, protectionDomain);
@@ -769,33 +882,29 @@
return true;
}
- private synchronized void checkCerts(String name, CodeSource cs) {
+ private void checkCerts(String name, CodeSource cs) {
int i = name.lastIndexOf('.');
String pname = (i == -1) ? "" : name.substring(0, i);
- Certificate[] pcerts = package2certs.get(pname);
- if (pcerts == null) {
- // first class in this package gets to define which
- // certificates must be the same for all other classes
- // in this package
- if (cs != null) {
- pcerts = cs.getCertificates();
+
+ Certificate[] certs = null;
+ if (cs != null) {
+ certs = cs.getCertificates();
+ }
+ Certificate[] pcerts = null;
+ if (parallelLockMap == null) {
+ synchronized (this) {
+ pcerts = package2certs.get(pname);
+ if (pcerts == null) {
+ package2certs.put(pname, (certs == null? nocerts:certs));
+ }
}
- if (pcerts == null) {
- if (nocerts == null)
- nocerts = new Certificate[0];
- pcerts = nocerts;
- }
- package2certs.put(pname, pcerts);
} else {
- Certificate[] certs = null;
- if (cs != null) {
- certs = cs.getCertificates();
- }
-
- if (!compareCerts(pcerts, certs)) {
- throw new SecurityException("class \""+ name +
- "\"'s signer information does not match signer information of other classes in the same package");
- }
+ pcerts = ((ConcurrentHashMap<String, Certificate[]>)package2certs).
+ putIfAbsent(pname, (certs == null? nocerts:certs));
+ }
+ if (pcerts != null && !compareCerts(pcerts, certs)) {
+ throw new SecurityException("class \""+ name +
+ "\"'s signer information does not match signer information of other classes in the same package");
}
}
@@ -1075,6 +1184,47 @@
return java.util.Collections.emptyEnumeration();
}
+ // index 0: java.lang.ClassLoader.class
+ // index 1: the immediate caller of index 0.
+ // index 2: the immediate caller of index 1.
+ private static native Class<? extends ClassLoader> getCaller(int index);
+
+ /**
+ * Registers the caller class loader as parallel capable.
+ * In order for the registration to succeed, all super classes
+ * of the caller class loader must also be registered as
+ * parallel capable when this method is called. </p>
+ * Note that once a class loader is registered as
+ * parallel capable, there is no way to change it back.
+ * In addition, registration should be done statically before
+ * any instance of the caller classloader being constructed. </p>
+ *
+ * @return true if the caller is successfully registered as
+ * parallel capable and false if otherwise.
+ *
+ * @since 1.7
+ */
+ protected static boolean registerAsParallelCapable() {
+ Class<? extends ClassLoader> caller = getCaller(1);
+ Class superCls = caller.getSuperclass();
+ boolean result = false;
+ // Explicit synchronization needed for composite action
+ synchronized (parallelLoaders) {
+ if (!parallelLoaders.contains(caller)) {
+ if (parallelLoaders.contains(superCls)) {
+ // register the immediate caller as parallel capable
+ // if and only if all of its super classes are.
+ // Note: given current classloading sequence, if
+ // the immediate super class is parallel capable,
+ // all the super classes higher up must be too.
+ result = true;
+ parallelLoaders.add(caller);
+ }
+ } else result = true;
+ }
+ return result;
+ }
+
/**
* Find a resource of the specified name from the search path used to load
* classes. This method locates the resource through the system class
@@ -1141,7 +1291,8 @@
private static Enumeration<URL> getBootstrapResources(String name)
throws IOException
{
- final Enumeration<Resource> e = getBootstrapClassPath().getResources(name);
+ final Enumeration<Resource> e =
+ getBootstrapClassPath().getResources(name);
return new Enumeration<URL> () {
public URL nextElement() {
return e.nextElement().getURL();
@@ -1377,9 +1528,11 @@
}
// The class loader for the system
+ // @GuardedBy("ClassLoader.class")
private static ClassLoader scl;
// Set to true once the system class loader has been set
+ // @GuardedBy("ClassLoader.class")
private static boolean sclSet;
@@ -1592,19 +1745,6 @@
}
}
- // The "default" domain. Set as the default ProtectionDomain on newly
- // created classes.
- private ProtectionDomain defaultDomain = null;
-
- // Returns (and initializes) the default domain.
- private synchronized ProtectionDomain getDefaultDomain() {
- if (defaultDomain == null) {
- CodeSource cs = new CodeSource(null, (Certificate[]) null);
- defaultDomain = new ProtectionDomain(cs, null, this, null);
- }
- return defaultDomain;
- }
-
// All native library names we've loaded.
private static Vector<String> loadedLibraryNames
= new Vector<String>();
@@ -1622,8 +1762,8 @@
= new Stack<NativeLibrary>();
// The paths searched for libraries
- static private String usr_paths[];
- static private String sys_paths[];
+ private static String usr_paths[];
+ private static String sys_paths[];
private static String[] initializePath(String propname) {
String ldpath = System.getProperty(propname, "");
@@ -1803,7 +1943,10 @@
// -- Assertion management --
+ final Object assertionLock;
+
// The default toggle for assertion checking.
+ // @GuardedBy("assertionLock")
private boolean defaultAssertionStatus = false;
// Maps String packageName to Boolean package default assertion status Note
@@ -1811,12 +1954,14 @@
// is null then we are delegating assertion status queries to the VM, i.e.,
// none of this ClassLoader's assertion status modification methods have
// been invoked.
+ // @GuardedBy("assertionLock")
private Map<String, Boolean> packageAssertionStatus = null;
// Maps String fullyQualifiedClassName to Boolean assertionStatus If this
// field is null then we are delegating assertion status queries to the VM,
// i.e., none of this ClassLoader's assertion status modification methods
// have been invoked.
+ // @GuardedBy("assertionLock")
Map<String, Boolean> classAssertionStatus = null;
/**
@@ -1834,11 +1979,13 @@
*
* @since 1.4
*/
- public synchronized void setDefaultAssertionStatus(boolean enabled) {
- if (classAssertionStatus == null)
- initializeJavaAssertionMaps();
+ public void setDefaultAssertionStatus(boolean enabled) {
+ synchronized (assertionLock) {
+ if (classAssertionStatus == null)
+ initializeJavaAssertionMaps();
- defaultAssertionStatus = enabled;
+ defaultAssertionStatus = enabled;
+ }
}
/**
@@ -1878,13 +2025,14 @@
*
* @since 1.4
*/
- public synchronized void setPackageAssertionStatus(String packageName,
- boolean enabled)
- {
- if (packageAssertionStatus == null)
- initializeJavaAssertionMaps();
+ public void setPackageAssertionStatus(String packageName,
+ boolean enabled) {
+ synchronized (assertionLock) {
+ if (packageAssertionStatus == null)
+ initializeJavaAssertionMaps();
- packageAssertionStatus.put(packageName, enabled);
+ packageAssertionStatus.put(packageName, enabled);
+ }
}
/**
@@ -1909,13 +2057,13 @@
*
* @since 1.4
*/
- public synchronized void setClassAssertionStatus(String className,
- boolean enabled)
- {
- if (classAssertionStatus == null)
- initializeJavaAssertionMaps();
+ public void setClassAssertionStatus(String className, boolean enabled) {
+ synchronized (assertionLock) {
+ if (classAssertionStatus == null)
+ initializeJavaAssertionMaps();
- classAssertionStatus.put(className, enabled);
+ classAssertionStatus.put(className, enabled);
+ }
}
/**
@@ -1928,15 +2076,16 @@
*
* @since 1.4
*/
- public synchronized void clearAssertionStatus() {
+ public void clearAssertionStatus() {
/*
* Whether or not "Java assertion maps" are initialized, set
* them to empty maps, effectively ignoring any present settings.
*/
- classAssertionStatus = new HashMap<String, Boolean>();
- packageAssertionStatus = new HashMap<String, Boolean>();
-
- defaultAssertionStatus = false;
+ synchronized (assertionLock) {
+ classAssertionStatus = new HashMap<String, Boolean>();
+ packageAssertionStatus = new HashMap<String, Boolean>();
+ defaultAssertionStatus = false;
+ }
}
/**
@@ -1961,39 +2110,40 @@
*
* @since 1.4
*/
- synchronized boolean desiredAssertionStatus(String className) {
- Boolean result;
-
- // assert classAssertionStatus != null;
- // assert packageAssertionStatus != null;
+ boolean desiredAssertionStatus(String className) {
+ synchronized (assertionLock) {
+ // assert classAssertionStatus != null;
+ // assert packageAssertionStatus != null;
- // Check for a class entry
- result = classAssertionStatus.get(className);
- if (result != null)
- return result.booleanValue();
-
- // Check for most specific package entry
- int dotIndex = className.lastIndexOf(".");
- if (dotIndex < 0) { // default package
- result = packageAssertionStatus.get(null);
+ // Check for a class entry
+ Boolean result = classAssertionStatus.get(className);
if (result != null)
return result.booleanValue();
+
+ // Check for most specific package entry
+ int dotIndex = className.lastIndexOf(".");
+ if (dotIndex < 0) { // default package
+ result = packageAssertionStatus.get(null);
+ if (result != null)
+ return result.booleanValue();
+ }
+ while(dotIndex > 0) {
+ className = className.substring(0, dotIndex);
+ result = packageAssertionStatus.get(className);
+ if (result != null)
+ return result.booleanValue();
+ dotIndex = className.lastIndexOf(".", dotIndex-1);
+ }
+
+ // Return the classloader default
+ return defaultAssertionStatus;
}
- while(dotIndex > 0) {
- className = className.substring(0, dotIndex);
- result = packageAssertionStatus.get(className);
- if (result != null)
- return result.booleanValue();
- dotIndex = className.lastIndexOf(".", dotIndex-1);
- }
-
- // Return the classloader default
- return defaultAssertionStatus;
}
// Set up the assertions with information provided by the VM.
+ // Note: Should only be called inside a synchronized block
private void initializeJavaAssertionMaps() {
- // assert Thread.holdsLock(this);
+ // assert Thread.holdsLock(assertionLock);
classAssertionStatus = new HashMap<String, Boolean>();
packageAssertionStatus = new HashMap<String, Boolean>();
--- a/jdk/src/share/classes/java/net/URLClassLoader.java Mon Apr 06 11:29:03 2009 +0100
+++ b/jdk/src/share/classes/java/net/URLClassLoader.java Mon Apr 06 18:46:20 2009 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -74,10 +74,10 @@
*/
public class URLClassLoader extends SecureClassLoader implements Closeable {
/* The search path for classes and resources */
- URLClassPath ucp;
+ private final URLClassPath ucp;
/* The context to be used when loading classes and resources */
- private AccessControlContext acc;
+ private final AccessControlContext acc;
/**
* Constructs a new URLClassLoader for the given URLs. The URLs will be
@@ -105,7 +105,19 @@
security.checkCreateClassLoader();
}
ucp = new URLClassPath(urls);
- acc = AccessController.getContext();
+ this.acc = AccessController.getContext();
+ }
+
+ URLClassLoader(URL[] urls, ClassLoader parent,
+ AccessControlContext acc) {
+ super(parent);
+ // this is to make the stack depth consistent with 1.1
+ SecurityManager security = System.getSecurityManager();
+ if (security != null) {
+ security.checkCreateClassLoader();
+ }
+ ucp = new URLClassPath(urls);
+ this.acc = acc;
}
/**
@@ -136,7 +148,18 @@
security.checkCreateClassLoader();
}
ucp = new URLClassPath(urls);
- acc = AccessController.getContext();
+ this.acc = AccessController.getContext();
+ }
+
+ URLClassLoader(URL[] urls, AccessControlContext acc) {
+ super();
+ // this is to make the stack depth consistent with 1.1
+ SecurityManager security = System.getSecurityManager();
+ if (security != null) {
+ security.checkCreateClassLoader();
+ }
+ ucp = new URLClassPath(urls);
+ this.acc = acc;
}
/**
@@ -599,17 +622,14 @@
public static URLClassLoader newInstance(final URL[] urls,
final ClassLoader parent) {
// Save the caller's context
- AccessControlContext acc = AccessController.getContext();
+ final AccessControlContext acc = AccessController.getContext();
// Need a privileged block to create the class loader
URLClassLoader ucl = AccessController.doPrivileged(
new PrivilegedAction<URLClassLoader>() {
public URLClassLoader run() {
- return new FactoryURLClassLoader(urls, parent);
+ return new FactoryURLClassLoader(urls, parent, acc);
}
});
- // Now set the context on the loader using the one we saved,
- // not the one inside the privileged block...
- ucl.acc = acc;
return ucl;
}
@@ -626,18 +646,14 @@
*/
public static URLClassLoader newInstance(final URL[] urls) {
// Save the caller's context
- AccessControlContext acc = AccessController.getContext();
+ final AccessControlContext acc = AccessController.getContext();
// Need a privileged block to create the class loader
URLClassLoader ucl = AccessController.doPrivileged(
new PrivilegedAction<URLClassLoader>() {
public URLClassLoader run() {
- return new FactoryURLClassLoader(urls);
+ return new FactoryURLClassLoader(urls, acc);
}
});
-
- // Now set the context on the loader using the one we saved,
- // not the one inside the privileged block...
- ucl.acc = acc;
return ucl;
}
@@ -649,20 +665,26 @@
}
}
);
+ ClassLoader.registerAsParallelCapable();
}
}
final class FactoryURLClassLoader extends URLClassLoader {
- FactoryURLClassLoader(URL[] urls, ClassLoader parent) {
- super(urls, parent);
+ static {
+ ClassLoader.registerAsParallelCapable();
}
- FactoryURLClassLoader(URL[] urls) {
- super(urls);
+ FactoryURLClassLoader(URL[] urls, ClassLoader parent,
+ AccessControlContext acc) {
+ super(urls, parent, acc);
}
- public final synchronized Class loadClass(String name, boolean resolve)
+ FactoryURLClassLoader(URL[] urls, AccessControlContext acc) {
+ super(urls, acc);
+ }
+
+ public final Class loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
// First check if we have permission to access the package. This
--- a/jdk/src/share/classes/java/security/SecureClassLoader.java Mon Apr 06 11:29:03 2009 +0100
+++ b/jdk/src/share/classes/java/security/SecureClassLoader.java Mon Apr 06 18:46:20 2009 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -45,14 +45,19 @@
* succeed. Otherwise the object is not initialized and the object is
* useless.
*/
- private boolean initialized = false;
+ private final boolean initialized;
// HashMap that maps CodeSource to ProtectionDomain
- private HashMap<CodeSource, ProtectionDomain> pdcache =
+ // @GuardedBy("pdcache")
+ private final HashMap<CodeSource, ProtectionDomain> pdcache =
new HashMap<CodeSource, ProtectionDomain>(11);
private static final Debug debug = Debug.getInstance("scl");
+ static {
+ ClassLoader.registerAsParallelCapable();
+ }
+
/**
* Creates a new SecureClassLoader using the specified parent
* class loader for delegation.
@@ -136,10 +141,7 @@
byte[] b, int off, int len,
CodeSource cs)
{
- if (cs == null)
- return defineClass(name, b, off, len);
- else
- return defineClass(name, b, off, len, getProtectionDomain(cs));
+ return defineClass(name, b, off, len, getProtectionDomain(cs));
}
/**
@@ -172,10 +174,7 @@
protected final Class<?> defineClass(String name, java.nio.ByteBuffer b,
CodeSource cs)
{
- if (cs == null)
- return defineClass(name, b, (ProtectionDomain)null);
- else
- return defineClass(name, b, getProtectionDomain(cs));
+ return defineClass(name, b, getProtectionDomain(cs));
}
/**
@@ -209,12 +208,10 @@
if (pd == null) {
PermissionCollection perms = getPermissions(cs);
pd = new ProtectionDomain(cs, perms, this, null);
- if (pd != null) {
- pdcache.put(cs, pd);
- if (debug != null) {
- debug.println(" getPermissions "+ pd);
- debug.println("");
- }
+ pdcache.put(cs, pd);
+ if (debug != null) {
+ debug.println(" getPermissions "+ pd);
+ debug.println("");
}
}
}
--- a/jdk/src/share/classes/sun/misc/Launcher.java Mon Apr 06 11:29:03 2009 +0100
+++ b/jdk/src/share/classes/sun/misc/Launcher.java Mon Apr 06 18:46:20 2009 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1998-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -120,7 +120,10 @@
* The class loader used for loading installed extensions.
*/
static class ExtClassLoader extends URLClassLoader {
- private File[] dirs;
+
+ static {
+ ClassLoader.registerAsParallelCapable();
+ }
/**
* create an ExtClassLoader. The ExtClassLoader is created
@@ -146,12 +149,12 @@
}
});
} catch (java.security.PrivilegedActionException e) {
- throw (IOException) e.getException();
+ throw (IOException) e.getException();
}
}
void addExtURL(URL url) {
- super.addURL(url);
+ super.addURL(url);
}
/*
@@ -159,7 +162,6 @@
*/
public ExtClassLoader(File[] dirs) throws IOException {
super(getExtURLs(dirs), null, factory);
- this.dirs = dirs;
}
private static File[] getExtDirs() {
@@ -206,20 +208,27 @@
*/
public String findLibrary(String name) {
name = System.mapLibraryName(name);
- for (int i = 0; i < dirs.length; i++) {
- // Look in architecture-specific subdirectory first
- String arch = System.getProperty("os.arch");
- if (arch != null) {
- File file = new File(new File(dirs[i], arch), name);
+ URL[] urls = super.getURLs();
+ File prevDir = null;
+ for (int i = 0; i < urls.length; i++) {
+ // Get the ext directory from the URL
+ File dir = new File(urls[i].getPath()).getParentFile();
+ if (dir != null && !dir.equals(prevDir)) {
+ // Look in architecture-specific subdirectory first
+ String arch = System.getProperty("os.arch");
+ if (arch != null) {
+ File file = new File(new File(dir, arch), name);
+ if (file.exists()) {
+ return file.getAbsolutePath();
+ }
+ }
+ // Then check the extension directory
+ File file = new File(dir, name);
if (file.exists()) {
return file.getAbsolutePath();
}
}
- // Then check the extension directory
- File file = new File(dirs[i], name);
- if (file.exists()) {
- return file.getAbsolutePath();
- }
+ prevDir = dir;
}
return null;
}
@@ -248,6 +257,10 @@
*/
static class AppClassLoader extends URLClassLoader {
+ static {
+ ClassLoader.registerAsParallelCapable();
+ }
+
public static ClassLoader getAppClassLoader(final ClassLoader extcl)
throws IOException
{
@@ -281,7 +294,7 @@
/**
* Override loadClass so we can checkPackageAccess.
*/
- public synchronized Class loadClass(String name, boolean resolve)
+ public Class loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
int i = name.lastIndexOf('.');
--- a/jdk/src/share/native/java/lang/ClassLoader.c Mon Apr 06 11:29:03 2009 +0100
+++ b/jdk/src/share/native/java/lang/ClassLoader.c Mon Apr 06 18:46:20 2009 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright 1996-2005 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1996-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -437,3 +437,21 @@
(*env)->ReleaseStringUTFChars(env, name, cname);
return res;
}
+
+JNIEXPORT jobject JNICALL
+Java_java_lang_ClassLoader_getCaller(JNIEnv *env, jclass cls, jint index)
+{
+ jobjectArray jcallerStack;
+ int len;
+
+ jcallerStack = JVM_GetClassContext(env);
+ if ((*env)->ExceptionCheck(env)) {
+ return NULL;
+ }
+ len = (*env)->GetArrayLength(env, jcallerStack);
+ if (index < len) {
+ return (*env)->GetObjectArrayElement(env, jcallerStack, index);
+ }
+ return NULL;
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/ClassLoader/deadlock/Alice.java Mon Apr 06 18:46:20 2009 -0700
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package comSA;
+
+public class Alice extends comSB.SupAlice {
+ static {
+ System.out.println("comSA.Alice loaded");
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/ClassLoader/deadlock/Bob.java Mon Apr 06 18:46:20 2009 -0700
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package comSB;
+
+public class Bob extends comSA.SupBob {
+ static {
+ System.out.println("comSB.Bob loaded");
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/ClassLoader/deadlock/DelegatingLoader.java Mon Apr 06 18:46:20 2009 -0700
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.locks.*;
+import java.lang.reflect.*;
+
+public class DelegatingLoader extends URLClassLoader {
+
+ private DelegatingLoader delLoader;
+ private String[] delClasses;
+
+ static {
+ boolean supportParallel = false;
+ try {
+ Class c = Class.forName("java.lang.ClassLoader");
+ Method m = c.getDeclaredMethod("registerAsParallelCapable",
+ new Class[0]);
+ m.setAccessible(true);
+ Object result = (Boolean) m.invoke(null);
+ if (result instanceof Boolean) {
+ supportParallel = ((Boolean) result).booleanValue();
+ } else {
+ // Should never happen
+ System.out.println("Error: ClassLoader.registerAsParallelCapable() did not return a boolean!");
+ System.exit(1);
+ }
+ } catch (NoSuchMethodException nsme) {
+ System.out.println("No ClassLoader.registerAsParallelCapable() API");
+ } catch (NoSuchMethodError nsme2) {
+ System.out.println("No ClassLoader.registerAsParallelCapable() API");
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ // Exit immediately to indicate an error
+ System.exit(1);
+ }
+ System.out.println("Parallel ClassLoader registration: " +
+ supportParallel);
+ }
+
+ public DelegatingLoader(URL urls[]) {
+ super(urls);
+ System.out.println("DelegatingLoader using URL " + urls[0]);
+ }
+
+ public void setDelegate(String[] delClasses, DelegatingLoader delLoader) {
+ this.delClasses = delClasses;
+ this.delLoader = delLoader;
+ }
+
+ public Class loadClass(String className, boolean resolve)
+ throws ClassNotFoundException {
+ for (int i = 0; i < delClasses.length; i++) {
+ if (delClasses[i].equals(className)) {
+ Starter.log("Delegating class loading for " + className);
+ try {
+ Thread.sleep(500);
+ } catch (InterruptedException ie) {
+ return null;
+ }
+ return delLoader.loadClass(className, resolve);
+ }
+ }
+
+ Starter.log("Loading local class " + className);
+// synchronized (getClassLoadingLock(className)) {
+ return super.loadClass(className, resolve);
+// }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/ClassLoader/deadlock/Starter.java Mon Apr 06 18:46:20 2009 -0700
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+public class Starter implements Runnable {
+
+ private String id;
+ private DelegatingLoader dl;
+ private String startClass;
+
+ private static DelegatingLoader saLoader, sbLoader;
+
+ public static void log(String line) {
+ System.out.println(line);
+ }
+
+ public static void main(String[] args) {
+ URL[] urlsa = new URL[1];
+ URL[] urlsb = new URL[1];
+ try {
+ String testDir = System.getProperty("test.classes", ".");
+ String sep = System.getProperty("file.separator");
+ urlsa[0] = new URL("file://" + testDir + sep + "SA" + sep);
+ urlsb[0] = new URL("file://" + testDir + sep + "SB" + sep);
+ } catch (MalformedURLException e) {
+ e.printStackTrace();
+ }
+ // Set up Classloader delegation hierarchy
+ saLoader = new DelegatingLoader(urlsa);
+ sbLoader = new DelegatingLoader(urlsb);
+
+ String[] saClasses = { "comSA.SupBob", "comSA.Alice" };
+ String[] sbClasses = { "comSB.SupAlice", "comSB.Bob" };
+
+ saLoader.setDelegate(sbClasses, sbLoader);
+ sbLoader.setDelegate(saClasses, saLoader);
+
+ // test one-way delegate
+ String testType = args[0];
+ if (testType.equals("one-way")) {
+ test("comSA.Alice", "comSA.SupBob");
+ } else if (testType.equals("cross")) {
+ // test cross delegate
+ test("comSA.Alice", "comSB.Bob");
+ } else {
+ System.out.println("ERROR: unsupported - " + testType);
+ }
+ }
+
+ private static void test(String clsForSA, String clsForSB) {
+ Starter ia = new Starter("SA", saLoader, clsForSA);
+ Starter ib = new Starter("SB", sbLoader, clsForSB);
+ new Thread(ia).start();
+ new Thread(ib).start();
+ }
+
+ public static void sleep() {
+ try {
+ Thread.sleep(500);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ log("Thread interrupted");
+ }
+ }
+
+ private Starter(String id, DelegatingLoader dl, String startClass) {
+ this.id = id;
+ this.dl = dl;
+ this.startClass = startClass;
+ }
+
+ public void run() {
+ log("Spawned thread " + id + " running");
+ try {
+ // To mirror the WAS deadlock, need to ensure class load
+ // is routed via the VM.
+ Class.forName(startClass, true, dl);
+ } catch (ClassNotFoundException e) {
+ e.printStackTrace();
+ }
+ log("Thread " + id + " terminating");
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/ClassLoader/deadlock/SupAlice.java Mon Apr 06 18:46:20 2009 -0700
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package comSB;
+
+public class SupAlice {
+ static {
+ System.out.println("comSB.SupAlice loaded");
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/ClassLoader/deadlock/SupBob.java Mon Apr 06 18:46:20 2009 -0700
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package comSA;
+
+public class SupBob {
+ static {
+ System.out.println("comSA.SupBob loaded");
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/ClassLoader/deadlock/TestCrossDelegate.sh Mon Apr 06 18:46:20 2009 -0700
@@ -0,0 +1,105 @@
+#
+# Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# This code is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 2 only, as
+# published by the Free Software Foundation.
+#
+# This code is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# version 2 for more details (a copy is included in the LICENSE file that
+# accompanied this code).
+#
+# You should have received a copy of the GNU General Public License version
+# 2 along with this work; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+# CA 95054 USA or visit www.sun.com if you need additional information or
+# have any questions.
+#
+# @test
+# @bug 4735126
+# @summary (cl) ClassLoader.loadClass locks all instances in chain
+# when delegating
+#
+# @run shell/timeout=10 TestCrossDelegate.sh
+
+# if running by hand on windows, change TESTSRC and TESTCLASSES to "."
+if [ "${TESTSRC}" = "" ] ; then
+ TESTSRC=`pwd`
+fi
+if [ "${TESTCLASSES}" = "" ] ; then
+ TESTCLASSES=`pwd`
+fi
+
+# if running by hand on windows, change this to appropriate value
+if [ "${TESTJAVA}" = "" ] ; then
+ echo "TESTJAVA not set. Test cannot execute."
+ echo "FAILED!!!"
+ exit 1
+fi
+echo TESTSRC=${TESTSRC}
+echo TESTCLASSES=${TESTCLASSES}
+echo TESTJAVA=${TESTJAVA}
+echo ""
+
+# set platform-specific variables
+OS=`uname -s`
+case "$OS" in
+ SunOS )
+ FS="/"
+ ;;
+ Linux )
+ FS="/"
+ ;;
+ Windows* )
+ FS="\\"
+ ;;
+esac
+
+# compile test
+${TESTJAVA}${FS}bin${FS}javac \
+ -d ${TESTCLASSES} \
+ ${TESTSRC}${FS}Starter.java ${TESTSRC}${FS}DelegatingLoader.java
+
+STATUS=$?
+if [ ${STATUS} -ne 0 ]
+then
+ exit ${STATUS}
+fi
+
+# set up test
+${TESTJAVA}${FS}bin${FS}javac \
+ -d ${TESTCLASSES}${FS} \
+ ${TESTSRC}${FS}Alice.java ${TESTSRC}${FS}SupBob.java \
+ ${TESTSRC}${FS}Bob.java ${TESTSRC}${FS}SupAlice.java
+
+cd ${TESTCLASSES}
+DIRS="SA SB"
+for dir in $DIRS
+do
+ if [ -d ${dir} ]; then
+ rm -rf ${dir}
+ fi
+ mkdir ${dir}
+ mv com${dir} ${dir}
+done
+
+# run test
+${TESTJAVA}${FS}bin${FS}java \
+ -verbose:class -XX:+TraceClassLoading -cp . \
+ -Dtest.classes=${TESTCLASSES} \
+ Starter cross
+# -XX:+UnlockDiagnosticVMOptions -XX:+UnsyncloadClass \
+
+# save error status
+STATUS=$?
+
+# clean up
+rm -rf ${TESTCLASSES}${FS}SA ${TESTCLASSES}${FS}SB
+
+# return
+exit ${STATUS}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/ClassLoader/deadlock/TestOneWayDelegate.sh Mon Apr 06 18:46:20 2009 -0700
@@ -0,0 +1,105 @@
+#
+# Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# This code is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 2 only, as
+# published by the Free Software Foundation.
+#
+# This code is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# version 2 for more details (a copy is included in the LICENSE file that
+# accompanied this code).
+#
+# You should have received a copy of the GNU General Public License version
+# 2 along with this work; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+# CA 95054 USA or visit www.sun.com if you need additional information or
+# have any questions.
+#
+# @test
+# @bug 4735126
+# @summary (cl) ClassLoader.loadClass locks all instances in chain
+# when delegating
+#
+# @run shell/timeout=10 TestOneWayDelegate.sh
+
+# if running by hand on windows, change TESTSRC and TESTCLASSES to "."
+if [ "${TESTSRC}" = "" ] ; then
+ TESTSRC=`pwd`
+fi
+if [ "${TESTCLASSES}" = "" ] ; then
+ TESTCLASSES=`pwd`
+fi
+
+# if running by hand on windows, change this to appropriate value
+if [ "${TESTJAVA}" = "" ] ; then
+ echo "TESTJAVA not set. Test cannot execute."
+ echo "FAILED!!!"
+ exit 1
+fi
+echo TESTSRC=${TESTSRC}
+echo TESTCLASSES=${TESTCLASSES}
+echo TESTJAVA=${TESTJAVA}
+echo ""
+
+# set platform-specific variables
+OS=`uname -s`
+case "$OS" in
+ SunOS )
+ FS="/"
+ ;;
+ Linux )
+ FS="/"
+ ;;
+ Windows* )
+ FS="\\"
+ ;;
+esac
+
+# compile test
+${TESTJAVA}${FS}bin${FS}javac \
+ -d ${TESTCLASSES} \
+ ${TESTSRC}${FS}Starter.java ${TESTSRC}${FS}DelegatingLoader.java
+
+STATUS=$?
+if [ ${STATUS} -ne 0 ]
+then
+ exit ${STATUS}
+fi
+
+# set up test
+${TESTJAVA}${FS}bin${FS}javac \
+ -d ${TESTCLASSES}${FS} \
+ ${TESTSRC}${FS}Alice.java ${TESTSRC}${FS}SupBob.java \
+ ${TESTSRC}${FS}Bob.java ${TESTSRC}${FS}SupAlice.java
+
+cd ${TESTCLASSES}
+DIRS="SA SB"
+for dir in $DIRS
+do
+ if [ -d ${dir} ]; then
+ rm -rf ${dir}
+ fi
+ mkdir ${dir}
+ mv com${dir} ${dir}
+done
+
+# run test
+${TESTJAVA}${FS}bin${FS}java \
+ -verbose:class -XX:+TraceClassLoading -cp . \
+ -Dtest.classes=${TESTCLASSES} \
+ Starter one-way
+# -XX:+UnlockDiagnosticVMOptions -XX:+UnsyncloadClass \
+
+# save error status
+STATUS=$?
+
+# clean up
+rm -rf ${TESTCLASSES}${FS}SA ${TESTCLASSES}${FS}SB
+
+# return
+exit ${STATUS}