diff --git a/src/share/classes/java/lang/ClassLoader.java b/src/share/classes/java/lang/ClassLoader.java index 2e98092f63e97df487d28b768df506d957d6eb9d..925fdacce3fd969eb45ac9b9b5ff82fd69487885 100644 --- a/src/share/classes/java/lang/ClassLoader.java +++ b/src/share/classes/java/lang/ClassLoader.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, Azul Systems, 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 @@ -1467,6 +1468,17 @@ public abstract class ClassLoader { } } + /* + * Initialize default paths for native libraries search. + * Must be done early as JDK may load libraries during bootstrap. + * + * @see java.lang.System#initPhase1 + */ + static void initLibraryPaths() { + usr_paths = initializePath("java.library.path"); + sys_paths = initializePath("sun.boot.library.path"); + } + // Returns true if the specified class loader can be found in this class // loader's delegation chain. boolean isAncestor(ClassLoader cl) { @@ -1809,10 +1821,9 @@ public abstract class ClassLoader { boolean isAbsolute) { ClassLoader loader = (fromClass == null) ? null : fromClass.getClassLoader(); - if (sys_paths == null) { - usr_paths = initializePath("java.library.path"); - sys_paths = initializePath("sun.boot.library.path"); - } + assert sys_paths != null : "should be initialized at this point"; + assert usr_paths != null : "should be initialized at this point"; + if (isAbsolute) { if (loadLibrary0(fromClass, new File(name))) { return; @@ -1902,13 +1913,14 @@ public abstract class ClassLoader { name + " already loaded in another classloader"); } - /* If the library is being loaded (must be by the same thread, - * because Runtime.load and Runtime.loadLibrary are - * synchronous). The reason is can occur is that the JNI_OnLoad - * function can cause another loadLibrary invocation. + /* + * When a library is being loaded, JNI_OnLoad function can cause + * another loadLibrary invocation that should succeed. * - * Thus we can use a static stack to hold the list of libraries - * we are loading. + * We use a static stack to hold the list of libraries we are + * loading because this can happen only when called by the + * same thread because Runtime.load and Runtime.loadLibrary + * are synchronous. * * If there is a pending load operation for the library, we * immediately return success; otherwise, we raise diff --git a/src/share/classes/java/lang/Runtime.java b/src/share/classes/java/lang/Runtime.java index 9e53dc939ecbe93804ba6d42eedbefff7f2835d9..5039059149f102f9e2390fe17731a76244478a26 100644 --- a/src/share/classes/java/lang/Runtime.java +++ b/src/share/classes/java/lang/Runtime.java @@ -1,5 +1,6 @@ /* * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, Azul Systems, 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 @@ -797,7 +798,7 @@ public class Runtime { load0(Reflection.getCallerClass(), filename); } - synchronized void load0(Class fromClass, String filename) { + void load0(Class fromClass, String filename) { SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkLink(filename); @@ -858,14 +859,14 @@ public class Runtime { loadLibrary0(Reflection.getCallerClass(), libname); } - synchronized void loadLibrary0(Class fromClass, String libname) { + void loadLibrary0(Class fromClass, String libname) { SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkLink(libname); } if (libname.indexOf((int)File.separatorChar) != -1) { throw new UnsatisfiedLinkError( - "Directory separator should not appear in library name: " + libname); + "Directory separator should not appear in library name: " + libname); } ClassLoader.loadLibrary(fromClass, libname, false); } diff --git a/src/share/classes/java/lang/System.java b/src/share/classes/java/lang/System.java index b2747fa7a41bc60266d3811b04fe371e97baa35c..7bc235beff7410ba56dcf28f37237119430deed9 100644 --- a/src/share/classes/java/lang/System.java +++ b/src/share/classes/java/lang/System.java @@ -1192,6 +1192,8 @@ public final class System { setOut0(newPrintStream(fdOut, props.getProperty("sun.stdout.encoding"))); setErr0(newPrintStream(fdErr, props.getProperty("sun.stderr.encoding"))); + ClassLoader.initLibraryPaths(); + // Load the zip library now in order to keep java.util.zip.ZipFile // from trying to use itself to load this library later. loadLibrary("zip"); diff --git a/test/java/lang/Runtime/loadLibrary/LoadLibraryTest.java b/test/java/lang/Runtime/loadLibrary/LoadLibraryTest.java new file mode 100644 index 0000000000000000000000000000000000000000..62eac12e186aa3cacfc4f9308c8eedd3b0dcca21 --- /dev/null +++ b/test/java/lang/Runtime/loadLibrary/LoadLibraryTest.java @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2018, Amazon and/or its affiliates. All rights reserved. + * Copyright (c) 2019, Azul Systems, 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @bug 8231584 + * @library /lib/testlibrary + * @run main/othervm LoadLibraryTest + */ + +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.nio.file.Path; +import java.net.MalformedURLException; +import java.net.URLClassLoader; +import java.net.URL; + +public class LoadLibraryTest { + static Thread thread1 = null; + static Thread thread2 = null; + + static volatile boolean thread1Ready = false; + + private static final String TEST_SRC = System.getProperty("test.src"); + private static final Path SRC_DIR = Paths.get(TEST_SRC, "src"); + private static final Path CLS_DIR = Paths.get("classes"); + + static TestClassLoader loader; + static void someLibLoad() { + try { +/* + FileSystems.getDefault(); + + // jdk/jdk: loads directly from Bootstrap Classloader (doesn't take lock on Runtime) + java.net.NetworkInterface.getNetworkInterfaces(); + + System.out.println(jdk.net.ExtendedSocketOptions.SO_FLOW_SLA); +*/ + Class c = Class.forName("Target2", true, loader); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + static class TestClassLoader extends URLClassLoader { + boolean passed = false; + + public boolean passed() { + return passed; + } + + TestClassLoader() throws MalformedURLException { + super(new URL[] { new URL("file://" + CLS_DIR.toAbsolutePath().toString() + '/') }); + } + + public String findLibrary(String name) { + System.out.println("findLibrary " + name); + + if ("someLibrary".equals(name)) { + try { + synchronized(thread1) { + while(!thread1Ready) { + thread1.wait(); + } + thread1.notifyAll(); + } + + Thread.sleep(10000); + + System.out.println("Thread2 load"); + someLibLoad(); + + // no deadlock happened + passed = true; + } catch (Exception e) { + throw new RuntimeException(e); + } + return null; + } + + return super.findLibrary(name); + } + } + + + public static void main(String[] args) throws Exception { + loader = new TestClassLoader(); + + if (!CompilerUtils.compile(SRC_DIR, CLS_DIR)) { + throw new Exception("Can't compile"); + } + + thread1 = new Thread() { + public void run() { + try { + synchronized(this) { + thread1Ready = true; + thread1.notifyAll(); + thread1.wait(); + } + } catch(InterruptedException e) { + throw new RuntimeException(e); + } + + System.out.println("Thread1 load"); + someLibLoad(); + }; + }; + + thread2 = new Thread() { + public void run() { + try { + Class c = Class.forName("Target", true, loader); + System.out.println(c); + } catch (Exception e) { + throw new RuntimeException(e); + } + }; + }; + + thread1.setDaemon(true); + thread2.setDaemon(true); + + thread1.start(); + thread2.start(); + + thread1.join(); + thread2.join(); + + if (!loader.passed()) { + throw new RuntimeException("FAIL"); + } + } +} diff --git a/test/java/lang/Runtime/loadLibrary/src/Target.java b/test/java/lang/Runtime/loadLibrary/src/Target.java new file mode 100644 index 0000000000000000000000000000000000000000..fc51481053d40deb161c68d4180a1ea7a90308ae --- /dev/null +++ b/test/java/lang/Runtime/loadLibrary/src/Target.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2019, Azul Systems, 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +class Target { + static { + try { + System.loadLibrary("someLibrary"); + throw new RuntimeException("someLibrary was loaded"); + } catch (UnsatisfiedLinkError e) { + // expected: we do not have a someLibrary + } + } +} + diff --git a/test/java/lang/Runtime/loadLibrary/src/Target2.java b/test/java/lang/Runtime/loadLibrary/src/Target2.java new file mode 100644 index 0000000000000000000000000000000000000000..bc8dfc5e63beccaa1d5d8b5489adeaf99101a5e3 --- /dev/null +++ b/test/java/lang/Runtime/loadLibrary/src/Target2.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2019, Azul Systems, 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +class Target2 { + static { + System.loadLibrary("awt"); + } +} +