changeset 1092:45ff1a9d4edb

4735126: (cl) ClassLoader.loadClass locks all instances in chain when delegating Summary: Added support for parallel-capable class loaders Reviewed-by: alanb
author valeriep
date Mon, 06 Apr 2009 18:46:20 -0700
parents d89688532509
children 22b6e09960c1
files make/java/java/mapfile-vers src/share/classes/java/lang/Class.java src/share/classes/java/lang/ClassLoader.java src/share/classes/java/net/URLClassLoader.java src/share/classes/java/security/SecureClassLoader.java src/share/classes/sun/misc/Launcher.java src/share/native/java/lang/ClassLoader.c test/java/lang/ClassLoader/deadlock/Alice.java test/java/lang/ClassLoader/deadlock/Bob.java test/java/lang/ClassLoader/deadlock/DelegatingLoader.java test/java/lang/ClassLoader/deadlock/Starter.java test/java/lang/ClassLoader/deadlock/SupAlice.java test/java/lang/ClassLoader/deadlock/SupBob.java test/java/lang/ClassLoader/deadlock/TestCrossDelegate.sh test/java/lang/ClassLoader/deadlock/TestOneWayDelegate.sh
diffstat 15 files changed, 937 insertions(+), 212 deletions(-) [+]
line wrap: on
line diff
--- a/make/java/java/mapfile-vers	Mon Apr 06 11:29:03 2009 +0100
+++ b/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/src/share/classes/java/lang/Class.java	Mon Apr 06 11:29:03 2009 +0100
+++ b/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/src/share/classes/java/lang/ClassLoader.java	Mon Apr 06 11:29:03 2009 +0100
+++ b/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
+    // 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<?>>();
+    private final 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;
+    boolean desiredAssertionStatus(String className) {
+        synchronized (assertionLock) {
+            // assert classAssertionStatus   != null;
+            // assert packageAssertionStatus != null;
 
-        // 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/src/share/classes/java/net/URLClassLoader.java	Mon Apr 06 11:29:03 2009 +0100
+++ b/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/src/share/classes/java/security/SecureClassLoader.java	Mon Apr 06 11:29:03 2009 +0100
+++ b/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/src/share/classes/sun/misc/Launcher.java	Mon Apr 06 11:29:03 2009 +0100
+++ b/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/src/share/native/java/lang/ClassLoader.c	Mon Apr 06 11:29:03 2009 +0100
+++ b/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/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/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/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/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/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/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/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/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}