6440846: (cl) Deadlock between AppClassLoader and ExtClassLoader
authorvaleriep
Mon, 06 Apr 2009 18:52:03 -0700
changeset 2449 509945aa3d2a
parent 2448 1e8128f3ff61
child 2450 bf25489d659f
child 2584 a89e7cabf6fd
6440846: (cl) Deadlock between AppClassLoader and ExtClassLoader Summary: Fixed a deadlock between the two class loaders Reviewed-by: alanb
jdk/src/share/classes/sun/security/jca/ProviderConfig.java
jdk/test/java/security/Security/ClassLoaderDeadlock/CreateSerialized.java
jdk/test/java/security/Security/ClassLoaderDeadlock/Deadlock2.java
jdk/test/java/security/Security/ClassLoaderDeadlock/Deadlock2.sh
--- a/jdk/src/share/classes/sun/security/jca/ProviderConfig.java	Mon Apr 06 18:46:20 2009 -0700
+++ b/jdk/src/share/classes/sun/security/jca/ProviderConfig.java	Mon Apr 06 18:52:03 2009 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright 2003-2006 Sun Microsystems, Inc.  All Rights Reserved.
+ * Copyright 2003-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
@@ -60,37 +60,6 @@
     // use by doLoadProvider()
     private final static Class[] CL_STRING = { String.class };
 
-    // lock to use while loading a provider. it ensures that each provider
-    // is loaded only once and that we can detect recursion.
-    // NOTE that because of 4944382 we use the system classloader as lock.
-    // By using the same lock to load classes as to load providers we avoid
-    // deadlock due to lock ordering. However, this class may be initialized
-    // early in the startup when the system classloader has not yet been set
-    // up. Use a temporary lock object if that is the case.
-    // Any of this may break if the class loading implementation is changed.
-    private static volatile Object LOCK = new Object();
-
-    private static Object getLock() {
-        Object o = LOCK;
-        // check if lock is already set to the class loader
-        if (o instanceof ClassLoader) {
-            return o;
-        }
-        Object cl = AccessController.doPrivileged(
-                                new PrivilegedAction<Object>() {
-            public Object run() {
-                return ClassLoader.getSystemClassLoader();
-            }
-        });
-        // check if class loader initialized now (non-null)
-        if (cl != null) {
-            LOCK = cl;
-            o = cl;
-        }
-        return o;
-    }
-
-
     // name of the provider class
     private final String className;
 
@@ -194,7 +163,7 @@
     /**
      * Get the provider object. Loads the provider if it is not already loaded.
      */
-    Provider getProvider() {
+    synchronized Provider getProvider() {
         // volatile variable load
         Provider p = provider;
         if (p != null) {
@@ -203,30 +172,23 @@
         if (shouldLoad() == false) {
             return null;
         }
-        synchronized (getLock()) {
-            p = provider;
-            if (p != null) {
-                // loaded by another thread while we were blocked on lock
-                return p;
+        if (isLoading) {
+            // because this method is synchronized, this can only
+            // happen if there is recursion.
+            if (debug != null) {
+                debug.println("Recursion loading provider: " + this);
+                new Exception("Call trace").printStackTrace();
             }
-            if (isLoading) {
-                // because this method is synchronized, this can only
-                // happen if there is recursion.
-                if (debug != null) {
-                    debug.println("Recursion loading provider: " + this);
-                    new Exception("Call trace").printStackTrace();
-                }
-                return null;
-            }
-            try {
-                isLoading = true;
-                tries++;
-                p = doLoadProvider();
-            } finally {
-                isLoading = false;
-            }
-            provider = p;
+            return null;
         }
+        try {
+            isLoading = true;
+            tries++;
+            p = doLoadProvider();
+        } finally {
+            isLoading = false;
+        }
+        provider = p;
         return p;
     }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/security/Security/ClassLoaderDeadlock/CreateSerialized.java	Mon Apr 06 18:52:03 2009 -0700
@@ -0,0 +1,35 @@
+/*
+ * 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.io.*;
+import sun.misc.*;
+
+public class CreateSerialized {
+    public static void main(String[] args) throws Exception {
+        Object o = new com.sun.crypto.provider.SunJCE();
+
+        FileOutputStream fos = new FileOutputStream("object.tmp");
+        ObjectOutputStream objectOutputStream = new ObjectOutputStream(fos);
+        objectOutputStream.writeObject(o);
+        fos.close();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/security/Security/ClassLoaderDeadlock/Deadlock2.java	Mon Apr 06 18:52:03 2009 -0700
@@ -0,0 +1,70 @@
+/*
+ * 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.io.*;
+import javax.xml.parsers.DocumentBuilderFactory;
+import java.security.*;
+
+public class Deadlock2 {
+    public static void main(String[] args) throws Exception {
+        File file = new File("object.tmp");
+        final byte[] bytes = new byte[(int) file.length()];
+        FileInputStream fileInputStream = new FileInputStream(file);
+        int read = fileInputStream.read(bytes);
+        if (read != file.length()) {
+            throw new Exception("Didn't read all");
+        }
+        Thread.sleep(1000);
+
+        Runnable xmlRunnable = new Runnable() {
+                public void run() {
+                    try {
+                        DocumentBuilderFactory.newInstance();
+                    } catch (Exception e) {
+                        e.printStackTrace();
+                    }
+                }
+            };
+
+        Runnable readObjectRunnable = new Runnable() {
+                public void run() {
+                    try {
+                        ObjectInputStream objectInputStream =
+                            new ObjectInputStream(new ByteArrayInputStream(bytes));
+                        Object o = objectInputStream.readObject();
+                        System.out.println(o.getClass());
+                    } catch (Exception e) {
+                        e.printStackTrace();
+                    }
+                }
+            }
+
+        Thread thread1 = new Thread(readObjectRunnable, "Read Object");
+        Thread thread2 = new Thread(xmlRunnable, "XML");
+
+        thread1.start();
+        thread2.start();
+
+        thread1.join();
+        thread2.join();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/security/Security/ClassLoaderDeadlock/Deadlock2.sh	Mon Apr 06 18:52:03 2009 -0700
@@ -0,0 +1,99 @@
+#!/bin/sh
+
+#
+# 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 6440846
+# @summary make sure we do not deadlock between ExtClassLoader and AppClassLoader
+# @author Valerie Peng
+# @run shell/timeout=20 Deadlock2.sh
+
+# set a few environment variables so that the shell-script can run stand-alone
+# in the source directory
+
+if [ "${TESTSRC}" = "" ] ; then
+   TESTSRC="."
+fi
+
+if [ "${TESTCLASSES}" = "" ] ; then
+   TESTCLASSES="."
+fi
+
+if [ "${TESTJAVA}" = "" ] ; then
+   echo "TESTJAVA not set.  Test cannot execute."
+   echo "FAILED!!!"
+   exit 1
+fi
+
+# set platform-dependent variables
+OS=`uname -s`
+case "$OS" in
+  SunOS )
+    PATHSEP=":"
+    FILESEP="/"
+    ;;
+  Linux )
+    PATHSEP=":"
+    FILESEP="/"
+    ;;
+  Windows* )
+    PATHSEP=";"
+    FILESEP="\\"
+    ;;
+  * )
+    echo "Unrecognized system!"
+    exit 1;
+    ;;
+esac
+
+# remove old class files
+cd ${TESTCLASSES}
+rm -f Deadlock2*.class
+if [ -d testlib ] ; then
+    rm -rf testlib
+fi
+cp -r ${TESTJAVA}${FILESEP}lib${FILESEP}ext testlib
+
+# compile and package the test program
+${TESTJAVA}${FILESEP}bin${FILESEP}javac \
+    -d ${TESTCLASSES} \
+    ${TESTSRC}${FILESEP}CreateSerialized.java \
+    ${TESTSRC}${FILESEP}Deadlock2.java
+
+${TESTJAVA}${FILESEP}bin${FILESEP}jar \
+    -cvf testlib${FILESEP}Deadlock2.jar \
+    Deadlock2*.class
+
+rm Deadlock2*.class
+
+# create serialized object and run the test
+${TESTJAVA}${FILESEP}bin${FILESEP}java CreateSerialized
+${TESTJAVA}${FILESEP}bin${FILESEP}java -Djava.ext.dirs=${TESTCLASSES}${FILESEP}testlib Deadlock2
+STATUS=$?
+
+# clean up
+rm object.tmp CreateSerialized.class
+rm -rf testlib
+exit ${STATUS}