1 /* 2 * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /* 25 * @test 26 * @bug 8164512 8191360 27 * @requires vm.compMode != "Xcomp" 28 * @requires !vm.musl 29 * @summary verify if the native library is unloaded when the class loader is GC'ed 30 * @build p.Test 31 * @run main/othervm/native -Xcheck:jni NativeLibraryTest 32 */ 33 34 // Clarification on @requires declarations: 35 // Under musl, dlclose is a no-op. The static variable 'count' in libnative.c keeps 36 // its value across a GC and the check in Test.java fails. 37 38 import java.io.IOException; 39 import java.net.MalformedURLException; 40 import java.net.URL; 41 import java.net.URLClassLoader; 42 import java.nio.file.Files; 43 import java.nio.file.Path; 44 import java.nio.file.Paths; 45 46 public class NativeLibraryTest { 47 static final Path CLASSES = Paths.get("classes"); 48 static int unloadedCount = 0; 49 50 /* 51 * Called by JNI_OnUnload when the native library is unloaded 52 */ 53 static void nativeLibraryUnloaded() { 54 unloadedCount++; 55 } 56 57 public static void main(String... args) throws Exception { 58 setup(); 59 60 for (int count=1; count <= 5; count++) { 61 // create a class loader and load a native library 62 runTest(); 63 // unloading the class loader and native library 64 System.gc(); 65 // give Cleaner thread a chance to unload the native library 66 Thread.sleep(100); 67 68 // unloadedCount is incremented when the native library is unloaded 69 if (count != unloadedCount) { 70 throw new RuntimeException("Expected unloaded=" + count + 71 " but got=" + unloadedCount); 72 } 73 } 74 } 75 76 /* 77 * Loads p.Test class with a new class loader and its static initializer 78 * will load a native library. 79 * 80 * The class loader becomes unreachable when this method returns and 81 * the native library should be unloaded at some point after the class 82 * loader is garbage collected. 83 */ 84 static void runTest() throws Exception { 85 // invoke p.Test.run() that loads the native library 86 Runnable r = newTestRunnable(); 87 r.run(); 88 89 // reload the native library by the same class loader 90 r.run(); 91 92 // load the native library by another class loader 93 Runnable r1 = newTestRunnable(); 94 try { 95 r1.run(); 96 throw new RuntimeException("should fail to load the native library" + 97 " by another class loader"); 98 } catch (UnsatisfiedLinkError e) {} 99 } 100 101 /* 102 * Loads p.Test class with a new class loader and returns 103 * a Runnable instance. 104 */ 105 static Runnable newTestRunnable() throws Exception { 106 TestLoader loader = new TestLoader(); 107 Class<?> c = Class.forName("p.Test", true, loader); 108 return (Runnable) c.newInstance(); 109 } 110 111 static class TestLoader extends URLClassLoader { 112 static URL[] toURLs() { 113 try { 114 return new URL[] { CLASSES.toUri().toURL() }; 115 } catch (MalformedURLException e) { 116 throw new Error(e); 117 } 118 } 119 120 TestLoader() { 121 super("testloader", toURLs(), ClassLoader.getSystemClassLoader()); 122 } 123 } 124 125 /* 126 * move p/Test.class out from classpath to the scratch directory 127 */ 128 static void setup() throws IOException { 129 String dir = System.getProperty("test.classes", "."); 130 Path file = Paths.get("p", "Test.class"); 131 Files.createDirectories(CLASSES.resolve("p")); 132 Files.move(Paths.get(dir).resolve(file), 133 CLASSES.resolve("p").resolve("Test.class")); 134 } 135 }