Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-4710268

ClassLoader deadlock in OIDMap.java

XMLWordPrintable

    • 05
    • generic, sparc
    • generic, solaris_8



        Name: dk106046 Date: 07/01/2002

        The primordial and standard extension class loader sun.misc.Launcher$ExtClassLoader are both trying to lock themselves then the other simultaneously, leaving two threads deadlocked, both with one loader locked and waiting on the other. The problem is present on all platforms in 1.3.1 and 1.4.x.

        Details of the problem:

        Thread "P=886672:O=0:LT=0:port=4582" (TID:0x9016D0, sys_thread_t:0x436F4098, state:CW, native ID:0x2BC) prio=5
        Owns sun.misc.Launcher$AppClassLoader@901830/901838
        Blocked on sun.misc.Launcher$ExtClassLoader@901898/9018A0

        Thread "P=886672:O=0:CT" (TID:0x9019D8, sys_thread_t:0x7B9040, state:CW, native ID:0xA6C) prio=5
        Owns sun.misc.Launcher$ExtClassLoader@901898/9018A0
        Blocked on sun.misc.Launcher$AppClassLoader@901830/901838

        The first thread is following normal locking order for classloaders: Application->Extension->Primordial
        The second thread is attempting to get a lock in the reverse direction, and therefore is causing a deadlock.

        The second thread has the following stack trace:
            "P=886672:O=0:CT" (TID:0x9019D8, sys_thread_t:0x7B9040, state:CW, native ID:0xA6C) prio=5
                at java.lang.ClassLoader.loadClass(ClassLoader.java:445)
                at sun.security.x509.OIDMap$1.run(OIDMap.java:272)
                at java.security.AccessController.doPrivileged(Native Method)
                at sun.security.x509.OIDMap.loadOidClass(OIDMap.java:269)
                at sun.security.x509.OIDMap.getClass(OIDMap.java:252)
                at sun.security.x509.CertificateExtensions.parseExtension(CertificateExtensions.java:101)
                at sun.security.x509.CertificateExtensions.init(CertificateExtensions.java:94)
                at sun.security.x509.CertificateExtensions.<init>(CertificateExtensions.java:73)
                at sun.security.x509.X509CertInfo.parse(X509CertInfo.java:732)

        Here OIDMap.loadOidClass() explicitly requests the Application call loader using getSystemClassLoader(), and procedes to try to load the class with this class loader.

        Solution:

        The simple solution is to force OIDMap.loadOidClass() to load the class with the current class loader using Class.forName(), which is currently the default should getSystemClassLoader() fail to return a class loader.

        We have modified OIDMap.java as follows:

          private static Class loadOidClass(String className)
           throws ClassNotFoundException
            {
        Class extClass = null;
        final String clName = className;
        /*final ClassLoader loader = ClassLoader.getSystemClassLoader();

                if (loader != null) {
        tr y {
        extClass = (Class)AccessController.doPrivileged
        (new PrivilegedExceptionAction() {
        public Object run() throws ClassNotFoundException {
        return loader.loadClass(clName);
        }
        });
        } catch (PrivilegedActionException pae) {
        throw (ClassNotFoundException) pae.getException();
        }
        } else {*/
        extClass = Class.forName(clName);
        //}

        return extClass;
            }



        Testcase to demonstrate the deadlock (further jar files are also required, which are available on request):

        import java.util.*;
        import java.util.jar.*;
        import java.net.*;
        import java.io.*;

        public class CLLockup
        {
            public static void main (String[] args)
            {
                Vector v = new Vector ();
                
                // add jdk jars
                addJarFiles (System.getProperty ("java.home") + File.separator + "lib", v);
                
                Thread thrd = new Thread (new CLThread (v, null));
                thrd.start ();
                
                // get a random delay within 1/2 sec
                int delay = Math.abs (new java.util.Random ().nextInt () % 500);
                
                // sleep for delay
                
                try {
                Thread.sleep (delay);
                } catch (InterruptedException ie) {}
                
                
                // get default factory
                Object o = javax.net.ssl.SSLServerSocketFactory.getDefault ();
                System.out.println ("DF " + o + " loaded by " + o.getClass ().getClassLoader ());

                try
                {
                    
                // wait for the thread to end
                thrd.join ();
                
                } catch (InterruptedException ie) {}

                System.exit (0);
                
            }
            
            
            private static class CLThread implements Runnable
            {
                private URLClassLoader _ucl;
                private Vector _v;
                private CLThread (Vector v, URLClassLoader ucl)
                {
                    
                    Vector urls = new Vector ();
                    for (int i=0; i<v.size (); i++)
                        try {
                            urls.addElement (((File)v.elementAt (i)).toURL ());
                        } catch (Exception e)
                        {
                            e.printStackTrace ();
                        }

                    // create URL class loader
                    ucl = new URLClassLoader ((URL[])urls.toArray (new URL [urls.size ()]));
                    
                    this._ucl = ucl;
                    this._v = v;
                }
                
                public void run ()
                {
                    for (int i=0; i<_v.size (); i++)
                    {
                        int nLoaded = 0, errors = 0;
                        
                        JarFile jf = null;
                        try
                        {
                            
                        jf = new JarFile ((File)_v.elementAt (i));
                        
                        Enumeration e = jf.entries ();
                        
                        while (e.hasMoreElements ())
                        {
                            String name = ((JarEntry)e.nextElement ()).getName ();
                            
                            if (name.endsWith (".class") && name.indexOf (":") == -1)
                            {
                                String className = name.substring (0, name.length () - 6);
                                
                                className = className.replace ('/', '.');
                                
                                try {
                                // now load this class
                                _ucl.loadClass (className);
                                // Class.forName (className);
                                
                                nLoaded++;
                                } catch (Throwable t)
                                {
                                    // t.printStackTrace ();
                                    errors++;
                                }
                            }
                        }
                        
                        System.out.println (jf.getName () + " : loaded " + nLoaded + " errors " + errors);
                        
                        } catch (Exception e)
                        {
                            e.printStackTrace ();
                        }
                        
                    }
                }
            }
            
            private static void addJarFiles (String path, Vector v)
            {
                if (path == null || path.length () == 0 || v == null)
                    throw new IllegalArgumentException ("bad arg(s) to addJarFiles ()");
                
                File dir = new File (path);
                
                if (dir.exists () && dir.isDirectory ())
                {
                    File[] f = dir.listFiles ();
                    
                    for (int i=0; i<f.length; i++)
                    {
                        if (f [i].exists () && !f [i].isDirectory () && f [i].getPath ().toLowerCase ().endsWith (".jar"))
                            // is a jar file. add it
                            v.addElement (f [i]);
                    }
                }
            }

        }


        ======================================================================

              andreas Andreas Sterbenz
              dkorbel David Korbel (Inactive)
              Votes:
              0 Vote for this issue
              Watchers:
              0 Start watching this issue

                Created:
                Updated:
                Resolved:
                Imported:
                Indexed: