8164512: Replace ClassLoader use of finalizer with phantom reference to unload native library
Reviewed-by: alanb, bchristi, kbarrett, dholmes, plevart
This commit is contained in:
parent
46d230c9cc
commit
25032bad54
@ -130,8 +130,8 @@ SUNWprivate_1.1 {
|
|||||||
Java_java_lang_ClassLoader_defineClass2;
|
Java_java_lang_ClassLoader_defineClass2;
|
||||||
Java_java_lang_ClassLoader_findBuiltinLib;
|
Java_java_lang_ClassLoader_findBuiltinLib;
|
||||||
Java_java_lang_ClassLoader_findLoadedClass0;
|
Java_java_lang_ClassLoader_findLoadedClass0;
|
||||||
Java_java_lang_ClassLoader_00024NativeLibrary_find;
|
Java_java_lang_ClassLoader_00024NativeLibrary_findEntry;
|
||||||
Java_java_lang_ClassLoader_00024NativeLibrary_load;
|
Java_java_lang_ClassLoader_00024NativeLibrary_load0;
|
||||||
Java_java_lang_ClassLoader_00024NativeLibrary_unload;
|
Java_java_lang_ClassLoader_00024NativeLibrary_unload;
|
||||||
Java_java_lang_ClassLoader_registerNatives;
|
Java_java_lang_ClassLoader_registerNatives;
|
||||||
Java_java_lang_Double_longBitsToDouble;
|
Java_java_lang_Double_longBitsToDouble;
|
||||||
|
@ -48,8 +48,8 @@ text: .text%Java_java_io_FileInputStream_available0;
|
|||||||
text: .text%Java_java_io_FileInputStream_close0;
|
text: .text%Java_java_io_FileInputStream_close0;
|
||||||
text: .text%Java_java_lang_System_mapLibraryName;
|
text: .text%Java_java_lang_System_mapLibraryName;
|
||||||
text: .text%Java_java_io_UnixFileSystem_getBooleanAttributes0;
|
text: .text%Java_java_io_UnixFileSystem_getBooleanAttributes0;
|
||||||
text: .text%Java_java_lang_ClassLoader_00024NativeLibrary_load;
|
text: .text%Java_java_lang_ClassLoader_00024NativeLibrary_load0;
|
||||||
text: .text%Java_java_lang_ClassLoader_00024NativeLibrary_find;
|
text: .text%Java_java_lang_ClassLoader_00024NativeLibrary_findEntry;
|
||||||
text: .text%Java_java_security_AccessController_doPrivileged__Ljava_security_PrivilegedExceptionAction_2;
|
text: .text%Java_java_security_AccessController_doPrivileged__Ljava_security_PrivilegedExceptionAction_2;
|
||||||
text: .text%Java_java_io_UnixFileSystem_list;
|
text: .text%Java_java_io_UnixFileSystem_list;
|
||||||
text: .text%JNU_ClassString;
|
text: .text%JNU_ClassString;
|
||||||
|
@ -57,8 +57,8 @@ text: .text%JNU_CopyObjectArray;
|
|||||||
text: .text%Java_java_io_UnixFileSystem_getBooleanAttributes0;
|
text: .text%Java_java_io_UnixFileSystem_getBooleanAttributes0;
|
||||||
text: .text%Java_java_security_AccessController_doPrivileged__Ljava_security_PrivilegedExceptionAction_2Ljava_security_AccessControlContext_2;
|
text: .text%Java_java_security_AccessController_doPrivileged__Ljava_security_PrivilegedExceptionAction_2Ljava_security_AccessControlContext_2;
|
||||||
text: .text%Java_java_lang_System_mapLibraryName;
|
text: .text%Java_java_lang_System_mapLibraryName;
|
||||||
text: .text%Java_java_lang_ClassLoader_00024NativeLibrary_load;
|
text: .text%Java_java_lang_ClassLoader_00024NativeLibrary_load0;
|
||||||
text: .text%Java_java_lang_ClassLoader_00024NativeLibrary_find;
|
text: .text%Java_java_lang_ClassLoader_00024NativeLibrary_findEntry;
|
||||||
text: .text%Java_java_io_UnixFileSystem_getLength;
|
text: .text%Java_java_io_UnixFileSystem_getLength;
|
||||||
text: .text%Java_java_lang_Object_getClass;
|
text: .text%Java_java_lang_Object_getClass;
|
||||||
text: .text%Java_java_lang_ClassLoader_defineClass0;
|
text: .text%Java_java_lang_ClassLoader_defineClass0;
|
||||||
|
@ -50,8 +50,8 @@ text: .text%Java_java_lang_ClassLoader_findBootstrapClass;
|
|||||||
text: .text%Java_java_security_AccessController_doPrivileged__Ljava_security_PrivilegedExceptionAction_2Ljava_security_AccessControlContext_2;
|
text: .text%Java_java_security_AccessController_doPrivileged__Ljava_security_PrivilegedExceptionAction_2Ljava_security_AccessControlContext_2;
|
||||||
text: .text%Java_java_lang_System_mapLibraryName;
|
text: .text%Java_java_lang_System_mapLibraryName;
|
||||||
text: .text%cpchars: OUTPUTDIR/System.o;
|
text: .text%cpchars: OUTPUTDIR/System.o;
|
||||||
text: .text%Java_java_lang_ClassLoader_00024NativeLibrary_load;
|
text: .text%Java_java_lang_ClassLoader_00024NativeLibrary_load0;
|
||||||
text: .text%Java_java_lang_ClassLoader_00024NativeLibrary_find;
|
text: .text%Java_java_lang_ClassLoader_00024NativeLibrary_findEntry;
|
||||||
text: .text%Java_java_lang_Float_floatToRawIntBits;
|
text: .text%Java_java_lang_Float_floatToRawIntBits;
|
||||||
text: .text%Java_java_lang_Double_doubleToRawLongBits;
|
text: .text%Java_java_lang_Double_doubleToRawLongBits;
|
||||||
text: .text%Java_java_io_FileInputStream_open0;
|
text: .text%Java_java_io_FileInputStream_open0;
|
||||||
|
@ -44,6 +44,7 @@ $(eval $(call IncludeCustomExtension, test/JtregNativeJdk.gmk))
|
|||||||
# Add more directories here when needed.
|
# Add more directories here when needed.
|
||||||
BUILD_JDK_JTREG_NATIVE_SRC += \
|
BUILD_JDK_JTREG_NATIVE_SRC += \
|
||||||
$(TOPDIR)/test/jdk/native_sanity \
|
$(TOPDIR)/test/jdk/native_sanity \
|
||||||
|
$(TOPDIR)/test/jdk/java/lang/ClassLoader/nativeLibrary \
|
||||||
$(TOPDIR)/test/jdk/java/lang/String/nativeEncoding \
|
$(TOPDIR)/test/jdk/java/lang/String/nativeEncoding \
|
||||||
#
|
#
|
||||||
|
|
||||||
|
@ -37,17 +37,20 @@ import java.security.CodeSource;
|
|||||||
import java.security.PrivilegedAction;
|
import java.security.PrivilegedAction;
|
||||||
import java.security.ProtectionDomain;
|
import java.security.ProtectionDomain;
|
||||||
import java.security.cert.Certificate;
|
import java.security.cert.Certificate;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.Deque;
|
||||||
import java.util.Enumeration;
|
import java.util.Enumeration;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.Hashtable;
|
import java.util.Hashtable;
|
||||||
|
import java.util.LinkedList;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.NoSuchElementException;
|
import java.util.NoSuchElementException;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.Spliterator;
|
import java.util.Spliterator;
|
||||||
import java.util.Spliterators;
|
import java.util.Spliterators;
|
||||||
import java.util.Stack;
|
|
||||||
import java.util.Vector;
|
import java.util.Vector;
|
||||||
import java.util.WeakHashMap;
|
import java.util.WeakHashMap;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
@ -58,9 +61,9 @@ import java.util.stream.StreamSupport;
|
|||||||
import jdk.internal.perf.PerfCounter;
|
import jdk.internal.perf.PerfCounter;
|
||||||
import jdk.internal.loader.BootLoader;
|
import jdk.internal.loader.BootLoader;
|
||||||
import jdk.internal.loader.ClassLoaders;
|
import jdk.internal.loader.ClassLoaders;
|
||||||
import jdk.internal.misc.SharedSecrets;
|
|
||||||
import jdk.internal.misc.Unsafe;
|
import jdk.internal.misc.Unsafe;
|
||||||
import jdk.internal.misc.VM;
|
import jdk.internal.misc.VM;
|
||||||
|
import jdk.internal.ref.CleanerFactory;
|
||||||
import jdk.internal.reflect.CallerSensitive;
|
import jdk.internal.reflect.CallerSensitive;
|
||||||
import jdk.internal.reflect.Reflection;
|
import jdk.internal.reflect.Reflection;
|
||||||
import sun.reflect.misc.ReflectUtil;
|
import sun.reflect.misc.ReflectUtil;
|
||||||
@ -2375,75 +2378,161 @@ public abstract class ClassLoader {
|
|||||||
* @since 1.2
|
* @since 1.2
|
||||||
*/
|
*/
|
||||||
static class NativeLibrary {
|
static class NativeLibrary {
|
||||||
|
// the class from which the library is loaded, also indicates
|
||||||
|
// the loader this native library belongs.
|
||||||
|
final Class<?> fromClass;
|
||||||
|
// the canonicalized name of the native library.
|
||||||
|
// or static library name
|
||||||
|
final String name;
|
||||||
|
// Indicates if the native library is linked into the VM
|
||||||
|
final boolean isBuiltin;
|
||||||
|
|
||||||
// opaque handle to native library, used in native code.
|
// opaque handle to native library, used in native code.
|
||||||
long handle;
|
long handle;
|
||||||
// the version of JNI environment the native library requires.
|
// the version of JNI environment the native library requires.
|
||||||
private int jniVersion;
|
int jniVersion;
|
||||||
// the class from which the library is loaded, also indicates
|
|
||||||
// the loader this native library belongs.
|
|
||||||
private Class<?> fromClass;
|
|
||||||
// the canonicalized name of the native library.
|
|
||||||
// or static library name
|
|
||||||
String name;
|
|
||||||
// Indicates if the native library is linked into the VM
|
|
||||||
boolean isBuiltin;
|
|
||||||
// Indicates if the native library is loaded
|
|
||||||
boolean loaded;
|
|
||||||
native void load(String name, boolean isBuiltin);
|
|
||||||
|
|
||||||
native long find(String name);
|
native boolean load0(String name, boolean isBuiltin);
|
||||||
native void unload(String name, boolean isBuiltin);
|
|
||||||
|
|
||||||
public NativeLibrary(Class<?> fromClass, String name, boolean isBuiltin) {
|
native long findEntry(String name);
|
||||||
|
|
||||||
|
NativeLibrary(Class<?> fromClass, String name, boolean isBuiltin) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.fromClass = fromClass;
|
this.fromClass = fromClass;
|
||||||
this.isBuiltin = isBuiltin;
|
this.isBuiltin = isBuiltin;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
/*
|
||||||
protected void finalize() {
|
* Loads the native library and registers for cleanup when its
|
||||||
synchronized (loadedLibraryNames) {
|
* associated class loader is unloaded
|
||||||
if (fromClass.getClassLoader() != null && loaded) {
|
*/
|
||||||
this.fromClass = null; // no context when unloaded
|
boolean load() {
|
||||||
|
if (handle != 0) {
|
||||||
|
throw new InternalError("Native library " + name + " has been loaded");
|
||||||
|
}
|
||||||
|
|
||||||
/* remove the native library name */
|
if (!load0(name, isBuiltin)) return false;
|
||||||
int size = loadedLibraryNames.size();
|
|
||||||
for (int i = 0; i < size; i++) {
|
// register the class loader for cleanup when unloaded
|
||||||
if (name.equals(loadedLibraryNames.elementAt(i))) {
|
// built class loaders are never unloaded
|
||||||
loadedLibraryNames.removeElementAt(i);
|
ClassLoader loader = fromClass.getClassLoader();
|
||||||
break;
|
if (loader != null &&
|
||||||
|
loader != getBuiltinPlatformClassLoader() &&
|
||||||
|
loader != getBuiltinAppClassLoader()) {
|
||||||
|
CleanerFactory.cleaner().register(loader,
|
||||||
|
new Unloader(name, handle, isBuiltin));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean loadLibrary(Class<?> fromClass, String name, boolean isBuiltin) {
|
||||||
|
ClassLoader loader =
|
||||||
|
fromClass == null ? null : fromClass.getClassLoader();
|
||||||
|
|
||||||
|
synchronized (loadedLibraryNames) {
|
||||||
|
Map<String, NativeLibrary> libs =
|
||||||
|
loader != null ? loader.nativeLibraries() : systemNativeLibraries();
|
||||||
|
if (libs.containsKey(name)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loadedLibraryNames.contains(name)) {
|
||||||
|
throw new UnsatisfiedLinkError("Native Library " + name +
|
||||||
|
" already loaded in another classloader");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When a library is being loaded, JNI_OnLoad function can cause
|
||||||
|
* another loadLibrary invocation that should succeed.
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
* UnsatisfiedLinkError.
|
||||||
|
*/
|
||||||
|
for (NativeLibrary lib : nativeLibraryContext) {
|
||||||
|
if (name.equals(lib.name)) {
|
||||||
|
if (loader == lib.fromClass.getClassLoader()) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
throw new UnsatisfiedLinkError("Native Library " +
|
||||||
|
name + " is being loaded in another classloader");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* unload the library. */
|
}
|
||||||
ClassLoader.nativeLibraryContext.push(this);
|
NativeLibrary lib = new NativeLibrary(fromClass, name, isBuiltin);
|
||||||
|
// load the native library
|
||||||
|
nativeLibraryContext.push(lib);
|
||||||
|
try {
|
||||||
|
if (!lib.load()) return false;
|
||||||
|
} finally {
|
||||||
|
nativeLibraryContext.pop();
|
||||||
|
}
|
||||||
|
// register the loaded native library
|
||||||
|
loadedLibraryNames.add(name);
|
||||||
|
libs.put(name, lib);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invoked in the VM to determine the context class in JNI_OnLoad
|
||||||
|
// and JNI_OnUnload
|
||||||
|
static Class<?> getFromClass() {
|
||||||
|
return nativeLibraryContext.peek().fromClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
// native libraries being loaded
|
||||||
|
static Deque<NativeLibrary> nativeLibraryContext = new LinkedList<>();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The run() method will be invoked when this class loader becomes
|
||||||
|
* phantom reachable to unload the native library.
|
||||||
|
*/
|
||||||
|
static class Unloader implements Runnable {
|
||||||
|
// This represents the context when a native library is unloaded
|
||||||
|
// and getFromClass() will return null,
|
||||||
|
static final NativeLibrary UNLOADER =
|
||||||
|
new NativeLibrary(null, "dummy", false);
|
||||||
|
final String name;
|
||||||
|
final long handle;
|
||||||
|
final boolean isBuiltin;
|
||||||
|
|
||||||
|
Unloader(String name, long handle, boolean isBuiltin) {
|
||||||
|
if (handle == 0) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Invalid handle for native library " + name);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.name = name;
|
||||||
|
this.handle = handle;
|
||||||
|
this.isBuiltin = isBuiltin;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
synchronized (loadedLibraryNames) {
|
||||||
|
/* remove the native library name */
|
||||||
|
loadedLibraryNames.remove(name);
|
||||||
|
nativeLibraryContext.push(UNLOADER);
|
||||||
try {
|
try {
|
||||||
unload(name, isBuiltin);
|
unload(name, isBuiltin, handle);
|
||||||
} finally {
|
} finally {
|
||||||
ClassLoader.nativeLibraryContext.pop();
|
nativeLibraryContext.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Invoked in the VM to determine the context class in
|
|
||||||
// JNI_Load/JNI_Unload
|
// JNI FindClass expects the caller class if invoked from JNI_OnLoad
|
||||||
static Class<?> getFromClass() {
|
// and JNI_OnUnload is NativeLibrary class
|
||||||
return ClassLoader.nativeLibraryContext.peek().fromClass;
|
static native void unload(String name, boolean isBuiltin, long handle);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// All native library names we've loaded.
|
|
||||||
private static Vector<String> loadedLibraryNames = new Vector<>();
|
|
||||||
|
|
||||||
// Native libraries belonging to system classes.
|
|
||||||
private static Vector<NativeLibrary> systemNativeLibraries
|
|
||||||
= new Vector<>();
|
|
||||||
|
|
||||||
// Native libraries associated with the class loader.
|
|
||||||
private Vector<NativeLibrary> nativeLibraries = new Vector<>();
|
|
||||||
|
|
||||||
// native libraries being loaded/unloaded.
|
|
||||||
private static Stack<NativeLibrary> nativeLibraryContext = new Stack<>();
|
|
||||||
|
|
||||||
// The paths searched for libraries
|
// The paths searched for libraries
|
||||||
private static String usr_paths[];
|
private static String usr_paths[];
|
||||||
private static String sys_paths[];
|
private static String sys_paths[];
|
||||||
@ -2455,7 +2544,7 @@ public abstract class ClassLoader {
|
|||||||
int psCount = 0;
|
int psCount = 0;
|
||||||
|
|
||||||
if (ClassLoaderHelper.allowsQuotedPathElements &&
|
if (ClassLoaderHelper.allowsQuotedPathElements &&
|
||||||
ldPath.indexOf('\"') >= 0) {
|
ldPath.indexOf('\"') >= 0) {
|
||||||
// First, remove quotes put around quoted parts of paths.
|
// First, remove quotes put around quoted parts of paths.
|
||||||
// Second, use a quotation mark as a new path separator.
|
// Second, use a quotation mark as a new path separator.
|
||||||
// This will preserve any quoted old path separators.
|
// This will preserve any quoted old path separators.
|
||||||
@ -2465,7 +2554,7 @@ public abstract class ClassLoader {
|
|||||||
char ch = ldPath.charAt(i);
|
char ch = ldPath.charAt(i);
|
||||||
if (ch == '\"') {
|
if (ch == '\"') {
|
||||||
while (++i < ldLen &&
|
while (++i < ldLen &&
|
||||||
(ch = ldPath.charAt(i)) != '\"') {
|
(ch = ldPath.charAt(i)) != '\"') {
|
||||||
buf[bufLen++] = ch;
|
buf[bufLen++] = ch;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -2481,7 +2570,7 @@ public abstract class ClassLoader {
|
|||||||
ps = '\"';
|
ps = '\"';
|
||||||
} else {
|
} else {
|
||||||
for (int i = ldPath.indexOf(ps); i >= 0;
|
for (int i = ldPath.indexOf(ps); i >= 0;
|
||||||
i = ldPath.indexOf(ps, i + 1)) {
|
i = ldPath.indexOf(ps, i + 1)) {
|
||||||
psCount++;
|
psCount++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2491,11 +2580,11 @@ public abstract class ClassLoader {
|
|||||||
for (int j = 0; j < psCount; ++j) {
|
for (int j = 0; j < psCount; ++j) {
|
||||||
int pathEnd = ldPath.indexOf(ps, pathStart);
|
int pathEnd = ldPath.indexOf(ps, pathStart);
|
||||||
paths[j] = (pathStart < pathEnd) ?
|
paths[j] = (pathStart < pathEnd) ?
|
||||||
ldPath.substring(pathStart, pathEnd) : ".";
|
ldPath.substring(pathStart, pathEnd) : ".";
|
||||||
pathStart = pathEnd + 1;
|
pathStart = pathEnd + 1;
|
||||||
}
|
}
|
||||||
paths[psCount] = (pathStart < ldLen) ?
|
paths[psCount] = (pathStart < ldLen) ?
|
||||||
ldPath.substring(pathStart, ldLen) : ".";
|
ldPath.substring(pathStart, ldLen) : ".";
|
||||||
return paths;
|
return paths;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2520,7 +2609,7 @@ public abstract class ClassLoader {
|
|||||||
File libfile = new File(libfilename);
|
File libfile = new File(libfilename);
|
||||||
if (!libfile.isAbsolute()) {
|
if (!libfile.isAbsolute()) {
|
||||||
throw new UnsatisfiedLinkError(
|
throw new UnsatisfiedLinkError(
|
||||||
"ClassLoader.findLibrary failed to return an absolute path: " + libfilename);
|
"ClassLoader.findLibrary failed to return an absolute path: " + libfilename);
|
||||||
}
|
}
|
||||||
if (loadLibrary0(fromClass, libfile)) {
|
if (loadLibrary0(fromClass, libfile)) {
|
||||||
return;
|
return;
|
||||||
@ -2551,10 +2640,11 @@ public abstract class ClassLoader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Oops, it failed
|
// Oops, it failed
|
||||||
throw new UnsatisfiedLinkError("no " + name + " in java.library.path");
|
throw new UnsatisfiedLinkError("no " + name +
|
||||||
|
" in java.library.path: " + Arrays.toString(usr_paths));
|
||||||
}
|
}
|
||||||
|
|
||||||
static native String findBuiltinLib(String name);
|
private static native String findBuiltinLib(String name);
|
||||||
|
|
||||||
private static boolean loadLibrary0(Class<?> fromClass, final File file) {
|
private static boolean loadLibrary0(Class<?> fromClass, final File file) {
|
||||||
// Check to see if we're attempting to access a static library
|
// Check to see if we're attempting to access a static library
|
||||||
@ -2575,85 +2665,72 @@ public abstract class ClassLoader {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ClassLoader loader =
|
return NativeLibrary.loadLibrary(fromClass, name, isBuiltin);
|
||||||
(fromClass == null) ? null : fromClass.getClassLoader();
|
|
||||||
Vector<NativeLibrary> libs =
|
|
||||||
loader != null ? loader.nativeLibraries : systemNativeLibraries;
|
|
||||||
synchronized (libs) {
|
|
||||||
int size = libs.size();
|
|
||||||
for (int i = 0; i < size; i++) {
|
|
||||||
NativeLibrary lib = libs.elementAt(i);
|
|
||||||
if (name.equals(lib.name)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
synchronized (loadedLibraryNames) {
|
|
||||||
if (loadedLibraryNames.contains(name)) {
|
|
||||||
throw new UnsatisfiedLinkError
|
|
||||||
("Native Library " +
|
|
||||||
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.
|
|
||||||
*
|
|
||||||
* Thus we can use a static stack to hold the list of libraries
|
|
||||||
* we are loading.
|
|
||||||
*
|
|
||||||
* If there is a pending load operation for the library, we
|
|
||||||
* immediately return success; otherwise, we raise
|
|
||||||
* UnsatisfiedLinkError.
|
|
||||||
*/
|
|
||||||
int n = nativeLibraryContext.size();
|
|
||||||
for (int i = 0; i < n; i++) {
|
|
||||||
NativeLibrary lib = nativeLibraryContext.elementAt(i);
|
|
||||||
if (name.equals(lib.name)) {
|
|
||||||
if (loader == lib.fromClass.getClassLoader()) {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
throw new UnsatisfiedLinkError
|
|
||||||
("Native Library " +
|
|
||||||
name +
|
|
||||||
" is being loaded in another classloader");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
NativeLibrary lib = new NativeLibrary(fromClass, name, isBuiltin);
|
|
||||||
nativeLibraryContext.push(lib);
|
|
||||||
try {
|
|
||||||
lib.load(name, isBuiltin);
|
|
||||||
} finally {
|
|
||||||
nativeLibraryContext.pop();
|
|
||||||
}
|
|
||||||
if (lib.loaded) {
|
|
||||||
loadedLibraryNames.addElement(name);
|
|
||||||
libs.addElement(lib);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invoked in the VM class linking code.
|
/*
|
||||||
static long findNative(ClassLoader loader, String name) {
|
* Invoked in the VM class linking code.
|
||||||
Vector<NativeLibrary> libs =
|
*/
|
||||||
loader != null ? loader.nativeLibraries : systemNativeLibraries;
|
private static long findNative(ClassLoader loader, String entryName) {
|
||||||
synchronized (libs) {
|
Map<String, NativeLibrary> libs =
|
||||||
int size = libs.size();
|
loader != null ? loader.nativeLibraries() : systemNativeLibraries();
|
||||||
for (int i = 0; i < size; i++) {
|
if (libs.isEmpty())
|
||||||
NativeLibrary lib = libs.elementAt(i);
|
return 0;
|
||||||
long entry = lib.find(name);
|
|
||||||
if (entry != 0)
|
// the native libraries map may be updated in another thread
|
||||||
return entry;
|
// when a native library is being loaded. No symbol will be
|
||||||
}
|
// searched from it yet.
|
||||||
|
for (NativeLibrary lib : libs.values()) {
|
||||||
|
long entry = lib.findEntry(entryName);
|
||||||
|
if (entry != 0) return entry;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// All native library names we've loaded.
|
||||||
|
// This also serves as the lock to obtain nativeLibraries
|
||||||
|
// and write to nativeLibraryContext.
|
||||||
|
private static final Set<String> loadedLibraryNames = new HashSet<>();
|
||||||
|
|
||||||
|
// Native libraries belonging to system classes.
|
||||||
|
private static volatile Map<String, NativeLibrary> systemNativeLibraries;
|
||||||
|
|
||||||
|
// Native libraries associated with the class loader.
|
||||||
|
private volatile Map<String, NativeLibrary> nativeLibraries;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the native libraries map associated with bootstrap class loader
|
||||||
|
* This method will create the map at the first time when called.
|
||||||
|
*/
|
||||||
|
private static Map<String, NativeLibrary> systemNativeLibraries() {
|
||||||
|
Map<String, NativeLibrary> libs = systemNativeLibraries;
|
||||||
|
if (libs == null) {
|
||||||
|
synchronized (loadedLibraryNames) {
|
||||||
|
libs = systemNativeLibraries;
|
||||||
|
if (libs == null) {
|
||||||
|
libs = systemNativeLibraries = new ConcurrentHashMap<>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return libs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the native libraries map associated with this class loader
|
||||||
|
* This method will create the map at the first time when called.
|
||||||
|
*/
|
||||||
|
private Map<String, NativeLibrary> nativeLibraries() {
|
||||||
|
Map<String, NativeLibrary> libs = nativeLibraries;
|
||||||
|
if (libs == null) {
|
||||||
|
synchronized (loadedLibraryNames) {
|
||||||
|
libs = nativeLibraries;
|
||||||
|
if (libs == null) {
|
||||||
|
libs = nativeLibraries = new ConcurrentHashMap<>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return libs;
|
||||||
|
}
|
||||||
|
|
||||||
// -- Assertion management --
|
// -- Assertion management --
|
||||||
|
|
||||||
|
@ -765,7 +765,9 @@ public class Runtime {
|
|||||||
* with the VM, then the JNI_OnLoad_L function exported by the library
|
* with the VM, then the JNI_OnLoad_L function exported by the library
|
||||||
* is invoked rather than attempting to load a dynamic library.
|
* is invoked rather than attempting to load a dynamic library.
|
||||||
* A filename matching the argument does not have to exist in the file
|
* A filename matching the argument does not have to exist in the file
|
||||||
* system. See the JNI Specification for more details.
|
* system.
|
||||||
|
* See the <a href="{@docRoot}/../specs/jni/index.html"> JNI Specification</a>
|
||||||
|
* for more details.
|
||||||
*
|
*
|
||||||
* Otherwise, the filename argument is mapped to a native library image in
|
* Otherwise, the filename argument is mapped to a native library image in
|
||||||
* an implementation-dependent manner.
|
* an implementation-dependent manner.
|
||||||
@ -818,7 +820,8 @@ public class Runtime {
|
|||||||
* specific prefix, file extension or path. If a native library
|
* specific prefix, file extension or path. If a native library
|
||||||
* called {@code libname} is statically linked with the VM, then the
|
* called {@code libname} is statically linked with the VM, then the
|
||||||
* JNI_OnLoad_{@code libname} function exported by the library is invoked.
|
* JNI_OnLoad_{@code libname} function exported by the library is invoked.
|
||||||
* See the JNI Specification for more details.
|
* See the <a href="{@docRoot}/../specs/jni/index.html"> JNI Specification</a>
|
||||||
|
* for more details.
|
||||||
*
|
*
|
||||||
* Otherwise, the libname argument is loaded from a system library
|
* Otherwise, the libname argument is loaded from a system library
|
||||||
* location and mapped to a native library image in an implementation-
|
* location and mapped to a native library image in an implementation-
|
||||||
|
@ -1799,7 +1799,8 @@ public final class System {
|
|||||||
* is invoked rather than attempting to load a dynamic library.
|
* is invoked rather than attempting to load a dynamic library.
|
||||||
* A filename matching the argument does not have to exist in the
|
* A filename matching the argument does not have to exist in the
|
||||||
* file system.
|
* file system.
|
||||||
* See the JNI Specification for more details.
|
* See the <a href="{@docRoot}/../specs/jni/index.html"> JNI Specification</a>
|
||||||
|
* for more details.
|
||||||
*
|
*
|
||||||
* Otherwise, the filename argument is mapped to a native library image in
|
* Otherwise, the filename argument is mapped to a native library image in
|
||||||
* an implementation-dependent manner.
|
* an implementation-dependent manner.
|
||||||
@ -1835,7 +1836,8 @@ public final class System {
|
|||||||
* specific prefix, file extension or path. If a native library
|
* specific prefix, file extension or path. If a native library
|
||||||
* called <code>libname</code> is statically linked with the VM, then the
|
* called <code>libname</code> is statically linked with the VM, then the
|
||||||
* JNI_OnLoad_<code>libname</code> function exported by the library is invoked.
|
* JNI_OnLoad_<code>libname</code> function exported by the library is invoked.
|
||||||
* See the JNI Specification for more details.
|
* See the <a href="{@docRoot}/../specs/jni/index.html"> JNI Specification</a>
|
||||||
|
* for more details.
|
||||||
*
|
*
|
||||||
* Otherwise, the libname argument is loaded from a system library
|
* Otherwise, the libname argument is loaded from a system library
|
||||||
* location and mapped to a native library image in an implementation-
|
* location and mapped to a native library image in an implementation-
|
||||||
|
@ -260,7 +260,6 @@ Java_java_lang_ClassLoader_findLoadedClass0(JNIEnv *env, jobject loader,
|
|||||||
|
|
||||||
static jfieldID handleID;
|
static jfieldID handleID;
|
||||||
static jfieldID jniVersionID;
|
static jfieldID jniVersionID;
|
||||||
static jfieldID loadedID;
|
|
||||||
static void *procHandle;
|
static void *procHandle;
|
||||||
|
|
||||||
static jboolean initIDs(JNIEnv *env)
|
static jboolean initIDs(JNIEnv *env)
|
||||||
@ -276,9 +275,6 @@ static jboolean initIDs(JNIEnv *env)
|
|||||||
jniVersionID = (*env)->GetFieldID(env, this, "jniVersion", "I");
|
jniVersionID = (*env)->GetFieldID(env, this, "jniVersion", "I");
|
||||||
if (jniVersionID == 0)
|
if (jniVersionID == 0)
|
||||||
return JNI_FALSE;
|
return JNI_FALSE;
|
||||||
loadedID = (*env)->GetFieldID(env, this, "loaded", "Z");
|
|
||||||
if (loadedID == 0)
|
|
||||||
return JNI_FALSE;
|
|
||||||
procHandle = getProcessHandle();
|
procHandle = getProcessHandle();
|
||||||
}
|
}
|
||||||
return JNI_TRUE;
|
return JNI_TRUE;
|
||||||
@ -335,30 +331,31 @@ static void *findJniFunction(JNIEnv *env, void *handle,
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Class: java_lang_ClassLoader_NativeLibrary
|
* Class: java_lang_ClassLoader_NativeLibrary
|
||||||
* Method: load
|
* Method: load0
|
||||||
* Signature: (Ljava/lang/String;Z)V
|
* Signature: (Ljava/lang/String;Z)Z
|
||||||
*/
|
*/
|
||||||
JNIEXPORT void JNICALL
|
JNIEXPORT jboolean JNICALL
|
||||||
Java_java_lang_ClassLoader_00024NativeLibrary_load
|
Java_java_lang_ClassLoader_00024NativeLibrary_load0
|
||||||
(JNIEnv *env, jobject this, jstring name, jboolean isBuiltin)
|
(JNIEnv *env, jobject this, jstring name, jboolean isBuiltin)
|
||||||
{
|
{
|
||||||
const char *cname;
|
const char *cname;
|
||||||
jint jniVersion;
|
jint jniVersion;
|
||||||
jthrowable cause;
|
jthrowable cause;
|
||||||
void * handle;
|
void * handle;
|
||||||
|
jboolean loaded = JNI_FALSE;
|
||||||
|
|
||||||
if (!initIDs(env))
|
if (!initIDs(env))
|
||||||
return;
|
return JNI_FALSE;
|
||||||
|
|
||||||
cname = JNU_GetStringPlatformChars(env, name, 0);
|
cname = JNU_GetStringPlatformChars(env, name, 0);
|
||||||
if (cname == 0)
|
if (cname == 0)
|
||||||
return;
|
return JNI_FALSE;
|
||||||
handle = isBuiltin ? procHandle : JVM_LoadLibrary(cname);
|
handle = isBuiltin ? procHandle : JVM_LoadLibrary(cname);
|
||||||
if (handle) {
|
if (handle) {
|
||||||
JNI_OnLoad_t JNI_OnLoad;
|
JNI_OnLoad_t JNI_OnLoad;
|
||||||
JNI_OnLoad = (JNI_OnLoad_t)findJniFunction(env, handle,
|
JNI_OnLoad = (JNI_OnLoad_t)findJniFunction(env, handle,
|
||||||
isBuiltin ? cname : NULL,
|
isBuiltin ? cname : NULL,
|
||||||
JNI_TRUE);
|
JNI_TRUE);
|
||||||
if (JNI_OnLoad) {
|
if (JNI_OnLoad) {
|
||||||
JavaVM *jvm;
|
JavaVM *jvm;
|
||||||
(*env)->GetJavaVM(env, &jvm);
|
(*env)->GetJavaVM(env, &jvm);
|
||||||
@ -400,20 +397,21 @@ Java_java_lang_ClassLoader_00024NativeLibrary_load
|
|||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
(*env)->SetLongField(env, this, handleID, ptr_to_jlong(handle));
|
(*env)->SetLongField(env, this, handleID, ptr_to_jlong(handle));
|
||||||
(*env)->SetBooleanField(env, this, loadedID, JNI_TRUE);
|
loaded = JNI_TRUE;
|
||||||
|
|
||||||
done:
|
done:
|
||||||
JNU_ReleaseStringPlatformChars(env, name, cname);
|
JNU_ReleaseStringPlatformChars(env, name, cname);
|
||||||
|
return loaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Class: java_lang_ClassLoader_NativeLibrary
|
* Class: java_lang_ClassLoader_NativeLibrary
|
||||||
* Method: unload
|
* Method: unload
|
||||||
* Signature: (Z)V
|
* Signature: (Ljava/lang/String;ZJ)V
|
||||||
*/
|
*/
|
||||||
JNIEXPORT void JNICALL
|
JNIEXPORT void JNICALL
|
||||||
Java_java_lang_ClassLoader_00024NativeLibrary_unload
|
Java_java_lang_ClassLoader_00024NativeLibrary_unload
|
||||||
(JNIEnv *env, jobject this, jstring name, jboolean isBuiltin)
|
(JNIEnv *env, jclass cls, jstring name, jboolean isBuiltin, jlong address)
|
||||||
{
|
{
|
||||||
const char *onUnloadSymbols[] = JNI_ONUNLOAD_SYMBOLS;
|
const char *onUnloadSymbols[] = JNI_ONUNLOAD_SYMBOLS;
|
||||||
void *handle;
|
void *handle;
|
||||||
@ -426,10 +424,10 @@ Java_java_lang_ClassLoader_00024NativeLibrary_unload
|
|||||||
if (cname == NULL) {
|
if (cname == NULL) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
handle = jlong_to_ptr((*env)->GetLongField(env, this, handleID));
|
handle = jlong_to_ptr(address);
|
||||||
JNI_OnUnload = (JNI_OnUnload_t )findJniFunction(env, handle,
|
JNI_OnUnload = (JNI_OnUnload_t )findJniFunction(env, handle,
|
||||||
isBuiltin ? cname : NULL,
|
isBuiltin ? cname : NULL,
|
||||||
JNI_FALSE);
|
JNI_FALSE);
|
||||||
if (JNI_OnUnload) {
|
if (JNI_OnUnload) {
|
||||||
JavaVM *jvm;
|
JavaVM *jvm;
|
||||||
(*env)->GetJavaVM(env, &jvm);
|
(*env)->GetJavaVM(env, &jvm);
|
||||||
@ -443,11 +441,11 @@ Java_java_lang_ClassLoader_00024NativeLibrary_unload
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Class: java_lang_ClassLoader_NativeLibrary
|
* Class: java_lang_ClassLoader_NativeLibrary
|
||||||
* Method: find
|
* Method: findEntry
|
||||||
* Signature: (Ljava/lang/String;)J
|
* Signature: (Ljava/lang/String;)J
|
||||||
*/
|
*/
|
||||||
JNIEXPORT jlong JNICALL
|
JNIEXPORT jlong JNICALL
|
||||||
Java_java_lang_ClassLoader_00024NativeLibrary_find
|
Java_java_lang_ClassLoader_00024NativeLibrary_findEntry
|
||||||
(JNIEnv *env, jobject this, jstring name)
|
(JNIEnv *env, jobject this, jstring name)
|
||||||
{
|
{
|
||||||
jlong handle;
|
jlong handle;
|
||||||
|
@ -0,0 +1,129 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2017, Oracle and/or its affiliates. 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 8164512
|
||||||
|
* @summary verify if the native library is unloaded when the class loader is GC'ed
|
||||||
|
* @build p.Test
|
||||||
|
* @run main/othervm/native -Xcheck:jni NativeLibraryTest
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLClassLoader;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
|
public class NativeLibraryTest {
|
||||||
|
static final Path CLASSES = Paths.get("classes");
|
||||||
|
static int unloadedCount = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Called by JNI_OnUnload when the native library is unloaded
|
||||||
|
*/
|
||||||
|
static void nativeLibraryUnloaded() {
|
||||||
|
unloadedCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String... args) throws Exception {
|
||||||
|
setup();
|
||||||
|
|
||||||
|
for (int count=1; count <= 5; count++) {
|
||||||
|
// create a class loader and load a native library
|
||||||
|
runTest();
|
||||||
|
// unloading the class loader and native library
|
||||||
|
System.gc();
|
||||||
|
// give Cleaner thread a chance to unload the native library
|
||||||
|
Thread.sleep(100);
|
||||||
|
|
||||||
|
// unloadedCount is incremented when the native library is unloaded
|
||||||
|
if (count != unloadedCount) {
|
||||||
|
throw new RuntimeException("Expected unloaded=" + count +
|
||||||
|
" but got=" + unloadedCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Loads p.Test class with a new class loader and its static initializer
|
||||||
|
* will load a native library.
|
||||||
|
*
|
||||||
|
* The class loader becomes unreachable when this method returns and
|
||||||
|
* the native library should be unloaded at some point after the class
|
||||||
|
* loader is garbage collected.
|
||||||
|
*/
|
||||||
|
static void runTest() throws Exception {
|
||||||
|
// invoke p.Test.run() that loads the native library
|
||||||
|
Runnable r = newTestRunnable();
|
||||||
|
r.run();
|
||||||
|
|
||||||
|
// reload the native library by the same class loader
|
||||||
|
r.run();
|
||||||
|
|
||||||
|
// load the native library by another class loader
|
||||||
|
Runnable r1 = newTestRunnable();
|
||||||
|
try {
|
||||||
|
r1.run();
|
||||||
|
throw new RuntimeException("should fail to load the native library" +
|
||||||
|
" by another class loader");
|
||||||
|
} catch (UnsatisfiedLinkError e) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Loads p.Test class with a new class loader and returns
|
||||||
|
* a Runnable instance.
|
||||||
|
*/
|
||||||
|
static Runnable newTestRunnable() throws Exception {
|
||||||
|
TestLoader loader = new TestLoader();
|
||||||
|
Class<?> c = Class.forName("p.Test", true, loader);
|
||||||
|
return (Runnable) c.newInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
static class TestLoader extends URLClassLoader {
|
||||||
|
static URL[] toURLs() {
|
||||||
|
try {
|
||||||
|
return new URL[] { CLASSES.toUri().toURL() };
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
throw new Error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TestLoader() {
|
||||||
|
super("testloader", toURLs(), ClassLoader.getSystemClassLoader());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* move p/Test.class out from classpath to the scratch directory
|
||||||
|
*/
|
||||||
|
static void setup() throws IOException {
|
||||||
|
String dir = System.getProperty("test.classes", ".");
|
||||||
|
Path file = Paths.get("p", "Test.class");
|
||||||
|
Files.createDirectories(CLASSES.resolve("p"));
|
||||||
|
Files.move(Paths.get(dir).resolve(file),
|
||||||
|
CLASSES.resolve("p").resolve("Test.class"));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2017, Oracle and/or its affiliates. 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "jni.h"
|
||||||
|
#include "jni_util.h"
|
||||||
|
|
||||||
|
static jint count = 0;
|
||||||
|
static jclass test_class;
|
||||||
|
static jint current_jni_version = JNI_VERSION_10;
|
||||||
|
|
||||||
|
JNIEXPORT jint JNICALL
|
||||||
|
JNI_OnLoad(JavaVM *vm, void *reserved) {
|
||||||
|
JNIEnv *env;
|
||||||
|
jclass cl;
|
||||||
|
|
||||||
|
(*vm)->GetEnv(vm, (void **) &env, current_jni_version);
|
||||||
|
|
||||||
|
cl = (*env)->FindClass(env, "NativeLibraryTest");
|
||||||
|
test_class = (*env)->NewGlobalRef(env, cl);
|
||||||
|
|
||||||
|
// increment the count when JNI_OnLoad is called
|
||||||
|
count++;
|
||||||
|
|
||||||
|
return current_jni_version;
|
||||||
|
}
|
||||||
|
|
||||||
|
JNIEXPORT void JNICALL
|
||||||
|
JNI_OnUnload(JavaVM *vm, void *reserved) {
|
||||||
|
JNIEnv *env;
|
||||||
|
jmethodID mid;
|
||||||
|
jclass cl;
|
||||||
|
|
||||||
|
(*vm)->GetEnv(vm, (void **) &env, current_jni_version);
|
||||||
|
mid = (*env)->GetStaticMethodID(env, test_class, "nativeLibraryUnloaded", "()V");
|
||||||
|
(*env)->CallStaticVoidMethod(env, test_class, mid);
|
||||||
|
if ((*env)->ExceptionCheck(env)) {
|
||||||
|
(*env)->ExceptionDescribe(env);
|
||||||
|
(*env)->FatalError(env, "Exception thrown");
|
||||||
|
}
|
||||||
|
|
||||||
|
cl = (*env)->FindClass(env, "p/Test");
|
||||||
|
if (cl != NULL) {
|
||||||
|
(*env)->FatalError(env, "p/Test class should not be found");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
JNIEXPORT jint JNICALL
|
||||||
|
Java_p_Test_count
|
||||||
|
(JNIEnv *env, jclass cls) {
|
||||||
|
return count;
|
||||||
|
}
|
37
test/jdk/java/lang/ClassLoader/nativeLibrary/p/Test.java
Normal file
37
test/jdk/java/lang/ClassLoader/nativeLibrary/p/Test.java
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2017, Oracle and/or its affiliates. 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.
|
||||||
|
*/
|
||||||
|
package p;
|
||||||
|
|
||||||
|
public class Test implements Runnable {
|
||||||
|
public static native int count();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests if the native library is loaded.
|
||||||
|
*/
|
||||||
|
public void run() {
|
||||||
|
System.loadLibrary("nativeLibraryTest");
|
||||||
|
if (count() != 1) {
|
||||||
|
throw new RuntimeException("Expected count = 1 but got " + count());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user