8339285: Test fails with assert(depth < max_critical_stack_depth) failed: can't have more than 10 critical frames

Reviewed-by: alanb
This commit is contained in:
Maurizio Cimadamore 2024-09-05 18:11:18 +00:00
parent 48d79431c9
commit 9e1af8cc7c
9 changed files with 241 additions and 41 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2001, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2001, 2024, 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
@ -46,8 +46,7 @@ static long calculate_number_of_pages_in_range(void* address, size_t len, size_t
return numPages;
}
JNIEXPORT jboolean JNICALL
Java_java_nio_MappedMemoryUtils_isLoaded0(JNIEnv *env, jobject obj, jlong address,
jboolean JNICALL MappedMemoryUtils_isLoaded0(JNIEnv *env, jobject obj, jlong address,
jlong len, jlong numPages)
{
jboolean loaded = JNI_TRUE;
@ -93,8 +92,7 @@ Java_java_nio_MappedMemoryUtils_isLoaded0(JNIEnv *env, jobject obj, jlong addres
}
JNIEXPORT void JNICALL
Java_java_nio_MappedMemoryUtils_load0(JNIEnv *env, jobject obj, jlong address,
void JNICALL MappedMemoryUtils_load0(JNIEnv *env, jobject obj, jlong address,
jlong len)
{
char *a = (char *)jlong_to_ptr(address);
@ -104,8 +102,7 @@ Java_java_nio_MappedMemoryUtils_load0(JNIEnv *env, jobject obj, jlong address,
}
}
JNIEXPORT void JNICALL
Java_java_nio_MappedMemoryUtils_unload0(JNIEnv *env, jobject obj, jlong address,
void JNICALL MappedMemoryUtils_unload0(JNIEnv *env, jobject obj, jlong address,
jlong len)
{
char *a = (char *)jlong_to_ptr(address);
@ -198,8 +195,7 @@ static int validate_msync_address(size_t address)
return 0;
}
JNIEXPORT void JNICALL
Java_java_nio_MappedMemoryUtils_force0(JNIEnv *env, jobject obj, jobject fdo,
void JNICALL MappedMemoryUtils_force0(JNIEnv *env, jobject obj, jobject fdo,
jlong address, jlong len)
{
void* a = (void *)jlong_to_ptr(address);
@ -218,3 +214,19 @@ Java_java_nio_MappedMemoryUtils_force0(JNIEnv *env, jobject obj, jobject fdo,
JNU_ThrowIOExceptionWithMessageAndLastError(env, "msync with parameter MS_SYNC failed");
}
}
#define FD "Ljava/io/FileDescriptor;"
static JNINativeMethod methods[] = {
{"isLoaded0", "(JJJ)Z", (void *)&MappedMemoryUtils_isLoaded0},
{"load0", "(JJ)V", (void *)&MappedMemoryUtils_load0},
{"unload0", "(JJ)V", (void *)&MappedMemoryUtils_unload0},
{"force0", "(" FD "JJ)V", (void *)&MappedMemoryUtils_force0},
};
JNIEXPORT void JNICALL
Java_java_nio_MappedMemoryUtils_registerNatives(JNIEnv *env, jclass cls)
{
(*env)->RegisterNatives(env, cls,
methods, sizeof(methods)/sizeof(methods[0]));
}

View File

@ -2450,7 +2450,8 @@ public abstract class ClassLoader {
* @param javaName the native method's declared name
*/
static long findNative(ClassLoader loader, Class<?> clazz, String entryName, String javaName) {
long addr = findNativeInternal(loader, entryName);
NativeLibraries nativeLibraries = nativeLibrariesFor(loader);
long addr = nativeLibraries.find(entryName);
if (addr != 0 && loader != null) {
Reflection.ensureNativeAccess(clazz, clazz, javaName, true);
}
@ -2462,11 +2463,11 @@ public abstract class ClassLoader {
* to avoid a restricted check, as that check has already been performed when
* obtaining the lookup.
*/
static long findNativeInternal(ClassLoader loader, String entryName) {
static NativeLibraries nativeLibrariesFor(ClassLoader loader) {
if (loader == null) {
return BootLoader.getNativeLibraries().find(entryName);
return BootLoader.getNativeLibraries();
} else {
return loader.libraries.find(entryName);
return loader.libraries;
}
}

View File

@ -70,6 +70,7 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Stream;
import jdk.internal.javac.Restricted;
import jdk.internal.loader.NativeLibraries;
import jdk.internal.logger.LoggerFinderLoader.TemporaryLoggerFinder;
import jdk.internal.misc.Blocker;
import jdk.internal.misc.CarrierThreadLocal;
@ -2662,8 +2663,8 @@ public final class System {
}
@Override
public long findNative(ClassLoader loader, String entry) {
return ClassLoader.findNativeInternal(loader, entry);
public NativeLibraries nativeLibrariesFor(ClassLoader loader) {
return ClassLoader.nativeLibrariesFor(loader);
}
@Override

View File

@ -31,6 +31,7 @@ import jdk.internal.foreign.MemorySessionImpl;
import jdk.internal.foreign.Utils;
import jdk.internal.javac.Restricted;
import jdk.internal.loader.BuiltinClassLoader;
import jdk.internal.loader.NativeLibraries;
import jdk.internal.loader.NativeLibrary;
import jdk.internal.loader.RawNativeLibraries;
import jdk.internal.reflect.CallerSensitive;
@ -256,7 +257,8 @@ public interface SymbolLookup {
if (Utils.containsNullChars(name)) return Optional.empty();
JavaLangAccess javaLangAccess = SharedSecrets.getJavaLangAccess();
// note: ClassLoader::findNative supports a null loader
long addr = javaLangAccess.findNative(loader, name);
NativeLibraries nativeLibraries = javaLangAccess.nativeLibrariesFor(loader);
long addr = nativeLibraries.find(name);
return addr == 0L ?
Optional.empty() :
Optional.of(MemorySegment.ofAddress(addr)

View File

@ -116,6 +116,18 @@ import jdk.internal.misc.Unsafe;
private static native void unload0(long address, long length);
private static native void force0(FileDescriptor fd, long address, long length) throws IOException;
/* Register the natives via the static initializer.
*
* This is required, as these native methods are "scoped methods" (see ScopedMemoryAccess).
* As such, it's better not to end up doing a full JNI lookup while in a scoped method context,
* as that will make the stack trace too deep.
*/
private static native void registerNatives();
static {
registerNatives();
isLoaded0(0, 0, 0);
}
// utility methods
// Returns the distance (in bytes) of the buffer start from the

View File

@ -29,6 +29,7 @@ import java.io.InputStream;
import java.io.PrintStream;
import java.lang.annotation.Annotation;
import java.lang.foreign.MemorySegment;
import java.lang.foreign.SymbolLookup;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
import java.lang.module.ModuleDescriptor;
@ -46,6 +47,7 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.RejectedExecutionException;
import java.util.stream.Stream;
import jdk.internal.loader.NativeLibraries;
import jdk.internal.misc.CarrierThreadLocal;
import jdk.internal.module.ServicesCatalog;
import jdk.internal.reflect.ConstantPool;
@ -478,7 +480,11 @@ public interface JavaLangAccess {
int getCharsUTF16(long i, int index, byte[] buf);
long findNative(ClassLoader loader, String entry);
/**
* Returns the {@link NativeLibraries} object associated with the provided class loader.
* This is used by {@link SymbolLookup#loaderLookup()}.
*/
NativeLibraries nativeLibrariesFor(ClassLoader loader);
/**
* Direct access to Shutdown.exit to avoid security manager checks

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2001, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2001, 2024, 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
@ -40,8 +40,7 @@ typedef unsigned char mincore_vec_t;
typedef char mincore_vec_t;
#endif
JNIEXPORT jboolean JNICALL
Java_java_nio_MappedMemoryUtils_isLoaded0(JNIEnv *env, jobject obj, jlong address,
jboolean JNICALL MappedMemoryUtils_isLoaded0(JNIEnv *env, jobject obj, jlong address,
jlong len, jlong numPages)
{
jboolean loaded = JNI_TRUE;
@ -80,8 +79,7 @@ Java_java_nio_MappedMemoryUtils_isLoaded0(JNIEnv *env, jobject obj, jlong addres
}
JNIEXPORT void JNICALL
Java_java_nio_MappedMemoryUtils_load0(JNIEnv *env, jobject obj, jlong address,
void JNICALL MappedMemoryUtils_load0(JNIEnv *env, jobject obj, jlong address,
jlong len)
{
char *a = (char *)jlong_to_ptr(address);
@ -91,8 +89,7 @@ Java_java_nio_MappedMemoryUtils_load0(JNIEnv *env, jobject obj, jlong address,
}
}
JNIEXPORT void JNICALL
Java_java_nio_MappedMemoryUtils_unload0(JNIEnv *env, jobject obj, jlong address,
void JNICALL MappedMemoryUtils_unload0(JNIEnv *env, jobject obj, jlong address,
jlong len)
{
char *a = (char *)jlong_to_ptr(address);
@ -102,8 +99,7 @@ Java_java_nio_MappedMemoryUtils_unload0(JNIEnv *env, jobject obj, jlong address,
}
}
JNIEXPORT void JNICALL
Java_java_nio_MappedMemoryUtils_force0(JNIEnv *env, jobject obj, jobject fdo,
void JNICALL MappedMemoryUtils_force0(JNIEnv *env, jobject obj, jobject fdo,
jlong address, jlong len)
{
void* a = (void *)jlong_to_ptr(address);
@ -112,3 +108,19 @@ Java_java_nio_MappedMemoryUtils_force0(JNIEnv *env, jobject obj, jobject fdo,
JNU_ThrowIOExceptionWithMessageAndLastError(env, "msync with parameter MS_SYNC failed");
}
}
#define FD "Ljava/io/FileDescriptor;"
static JNINativeMethod methods[] = {
{"isLoaded0", "(JJJ)Z", (void *)&MappedMemoryUtils_isLoaded0},
{"load0", "(JJ)V", (void *)&MappedMemoryUtils_load0},
{"unload0", "(JJ)V", (void *)&MappedMemoryUtils_unload0},
{"force0", "(" FD "JJ)V", (void *)&MappedMemoryUtils_force0},
};
JNIEXPORT void JNICALL
Java_java_nio_MappedMemoryUtils_registerNatives(JNIEnv *env, jclass cls)
{
(*env)->RegisterNatives(env, cls,
methods, sizeof(methods)/sizeof(methods[0]));
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2001, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2001, 2024, 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
@ -30,8 +30,7 @@
#include "java_nio_MappedMemoryUtils.h"
#include <stdlib.h>
JNIEXPORT jboolean JNICALL
Java_java_nio_MappedMemoryUtils_isLoaded0(JNIEnv *env, jobject obj, jlong address,
jboolean JNICALL MappedMemoryUtils_isLoaded0(JNIEnv *env, jobject obj, jlong address,
jlong len, jlong numPages)
{
jboolean loaded = JNI_FALSE;
@ -43,22 +42,19 @@ Java_java_nio_MappedMemoryUtils_isLoaded0(JNIEnv *env, jobject obj, jlong addres
return loaded;
}
JNIEXPORT void JNICALL
Java_java_nio_MappedMemoryUtils_load0(JNIEnv *env, jobject obj, jlong address,
void JNICALL MappedMemoryUtils_load0(JNIEnv *env, jobject obj, jlong address,
jlong len)
{
// no madvise available
}
JNIEXPORT void JNICALL
Java_java_nio_MappedMemoryUtils_unload0(JNIEnv *env, jobject obj, jlong address,
void JNICALL MappedMemoryUtils_unload0(JNIEnv *env, jobject obj, jlong address,
jlong len)
{
// no madvise available
}
JNIEXPORT void JNICALL
Java_java_nio_MappedMemoryUtils_force0(JNIEnv *env, jobject obj, jobject fdo,
void JNICALL MappedMemoryUtils_force0(JNIEnv *env, jobject obj, jobject fdo,
jlong address, jlong len)
{
void *a = (void *) jlong_to_ptr(address);
@ -106,3 +102,19 @@ Java_java_nio_MappedMemoryUtils_force0(JNIEnv *env, jobject obj, jobject fdo,
JNU_ThrowIOExceptionWithLastError(env, "Flush failed");
}
}
#define FD "Ljava/io/FileDescriptor;"
static JNINativeMethod methods[] = {
{"isLoaded0", "(JJJ)Z", (void *)&MappedMemoryUtils_isLoaded0},
{"load0", "(JJ)V", (void *)&MappedMemoryUtils_load0},
{"unload0", "(JJ)V", (void *)&MappedMemoryUtils_unload0},
{"force0", "(" FD "JJ)V", (void *)&MappedMemoryUtils_force0},
};
JNIEXPORT void JNICALL
Java_java_nio_MappedMemoryUtils_registerNatives(JNIEnv *env, jclass cls)
{
(*env)->RegisterNatives(env, cls,
methods, sizeof(methods)/sizeof(methods[0]));
}

View File

@ -0,0 +1,142 @@
/*
* Copyright (c) 2024, 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
* @requires vm.flavor != "zero"
* @modules java.base/jdk.internal.vm.annotation java.base/jdk.internal.misc
* @key randomness
* @run testng/othervm TestMappedHandshake
* @run testng/othervm -Xint TestMappedHandshake
* @run testng/othervm -XX:TieredStopAtLevel=1 TestMappedHandshake
* @run testng/othervm -XX:-TieredCompilation TestMappedHandshake
*/
import java.io.File;
import java.io.IOException;
import java.lang.foreign.Arena;
import java.lang.foreign.MemorySegment;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import org.testng.annotations.Test;
import static org.testng.Assert.*;
public class TestMappedHandshake {
static final int SEGMENT_SIZE = 1_000_000;
static final int ACCESS_START_DELAY_MILLIS = 100;
static final int POST_ACCESS_DELAY_MILLIS = 1;
static final int TIMED_RUN_TIME_SECONDS = 10;
static final int MAX_EXECUTOR_WAIT_SECONDS = 20;
static final int NUM_ACCESSORS = 5;
static final Path tempPath;
static {
try {
File file = File.createTempFile("buffer", "txt");
file.deleteOnExit();
tempPath = file.toPath();
Files.write(file.toPath(), new byte[SEGMENT_SIZE], StandardOpenOption.WRITE);
} catch (IOException ex) {
throw new ExceptionInInitializerError(ex);
}
}
@Test
public void testHandshake() throws InterruptedException, IOException {
try (FileChannel fileChannel = FileChannel.open(tempPath, StandardOpenOption.READ, StandardOpenOption.WRITE) ;
Arena arena = Arena.ofShared()) {
MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, SEGMENT_SIZE, arena);
ExecutorService accessExecutor = Executors.newFixedThreadPool(NUM_ACCESSORS + 1);
// start handshaker
accessExecutor.execute(new Handshaker());
Thread.sleep(ACCESS_START_DELAY_MILLIS);
// start accessors
for (int i = 0 ; i < NUM_ACCESSORS ; i++) {
accessExecutor.execute(new MappedSegmentAccessor(segment));
}
accessExecutor.shutdown();
assertTrue(accessExecutor.awaitTermination(MAX_EXECUTOR_WAIT_SECONDS, TimeUnit.SECONDS));
}
}
static abstract class TimedAction implements Runnable {
@Override
public void run() {
long start = System.currentTimeMillis();
while (true) {
try {
doAction();
} catch (Throwable ex) {
// ignore
} finally {
long elapsed = System.currentTimeMillis() - start;
if (elapsed > TIMED_RUN_TIME_SECONDS * 1000) {
break;
}
}
}
}
abstract void doAction() throws Throwable;
}
static class MappedSegmentAccessor extends TimedAction {
final MemorySegment segment;
MappedSegmentAccessor(MemorySegment segment) {
this.segment = segment;
}
@Override
void doAction() throws Throwable {
segment.load();
Thread.sleep(POST_ACCESS_DELAY_MILLIS);
segment.isLoaded();
Thread.sleep(POST_ACCESS_DELAY_MILLIS);
segment.unload();
Thread.sleep(POST_ACCESS_DELAY_MILLIS);
segment.force();
}
}
static class Handshaker extends TimedAction {
@Override
public void doAction() {
Arena.ofShared().close();
}
}
}