8317514: Ensure MemorySegment is initialized before touching NativeMemorySegmentImpl

Co-authored-by: Jorn Vernee <jvernee@openjdk.org>
Reviewed-by: jvernee
This commit is contained in:
Maurizio Cimadamore 2023-10-13 20:51:52 +00:00
parent 9622de2aa8
commit 7cb2e6d65b
12 changed files with 338 additions and 183 deletions

@ -44,9 +44,8 @@ import java.util.Spliterator;
import java.util.function.Consumer;
import java.util.stream.Stream;
import jdk.internal.foreign.AbstractMemorySegmentImpl;
import jdk.internal.foreign.HeapMemorySegmentImpl;
import jdk.internal.foreign.MemorySessionImpl;
import jdk.internal.foreign.NativeMemorySegmentImpl;
import jdk.internal.foreign.SegmentFactories;
import jdk.internal.foreign.StringSupport;
import jdk.internal.foreign.Utils;
import jdk.internal.javac.Restricted;
@ -1203,7 +1202,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl {
* @return a heap memory segment backed by a byte array.
*/
static MemorySegment ofArray(byte[] byteArray) {
return HeapMemorySegmentImpl.OfByte.fromArray(byteArray);
return SegmentFactories.fromArray(byteArray);
}
/**
@ -1215,7 +1214,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl {
* @return a heap memory segment backed by a char array.
*/
static MemorySegment ofArray(char[] charArray) {
return HeapMemorySegmentImpl.OfChar.fromArray(charArray);
return SegmentFactories.fromArray(charArray);
}
/**
@ -1227,7 +1226,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl {
* @return a heap memory segment backed by a short array.
*/
static MemorySegment ofArray(short[] shortArray) {
return HeapMemorySegmentImpl.OfShort.fromArray(shortArray);
return SegmentFactories.fromArray(shortArray);
}
/**
@ -1239,7 +1238,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl {
* @return a heap memory segment backed by an int array.
*/
static MemorySegment ofArray(int[] intArray) {
return HeapMemorySegmentImpl.OfInt.fromArray(intArray);
return SegmentFactories.fromArray(intArray);
}
/**
@ -1251,7 +1250,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl {
* @return a heap memory segment backed by a float array.
*/
static MemorySegment ofArray(float[] floatArray) {
return HeapMemorySegmentImpl.OfFloat.fromArray(floatArray);
return SegmentFactories.fromArray(floatArray);
}
/**
@ -1263,7 +1262,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl {
* @return a heap memory segment backed by a long array.
*/
static MemorySegment ofArray(long[] longArray) {
return HeapMemorySegmentImpl.OfLong.fromArray(longArray);
return SegmentFactories.fromArray(longArray);
}
/**
@ -1275,13 +1274,13 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl {
* @return a heap memory segment backed by a double array.
*/
static MemorySegment ofArray(double[] doubleArray) {
return HeapMemorySegmentImpl.OfDouble.fromArray(doubleArray);
return SegmentFactories.fromArray(doubleArray);
}
/**
* A zero-length native segment modelling the {@code NULL} address.
* A zero-length native segment modelling the {@code NULL} address. Equivalent to {@code MemorySegment.ofAddress(0L)}.
*/
MemorySegment NULL = new NativeMemorySegmentImpl();
MemorySegment NULL = MemorySegment.ofAddress(0L);
/**
* Creates a zero-length native segment from the given {@linkplain #address() address value}.
@ -1295,7 +1294,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl {
* @return a zero-length native segment with the given address.
*/
static MemorySegment ofAddress(long address) {
return NativeMemorySegmentImpl.makeNativeSegmentUnchecked(address, 0);
return SegmentFactories.makeNativeSegmentUnchecked(address, 0);
}
/**

@ -151,9 +151,9 @@ public abstract sealed class AbstractMemorySegmentImpl
}
if (!isNative()) throw new UnsupportedOperationException("Not a native segment");
Runnable action = cleanup != null ?
() -> cleanup.accept(NativeMemorySegmentImpl.makeNativeSegmentUnchecked(address(), newSize)) :
() -> cleanup.accept(SegmentFactories.makeNativeSegmentUnchecked(address(), newSize)) :
null;
return NativeMemorySegmentImpl.makeNativeSegmentUnchecked(address(), newSize,
return SegmentFactories.makeNativeSegmentUnchecked(address(), newSize,
(MemorySessionImpl)scope, action);
}

@ -52,7 +52,7 @@ public final class ArenaImpl implements Arena {
public MemorySegment allocateNoInit(long byteSize, long byteAlignment) {
Utils.checkAllocationSizeAndAlign(byteSize, byteAlignment);
return NativeMemorySegmentImpl.makeNativeSegmentNoZeroing(byteSize, byteAlignment, session, shouldReserveMemory);
return SegmentFactories.allocateSegment(byteSize, byteAlignment, session, shouldReserveMemory);
}
@Override

@ -26,7 +26,6 @@
package jdk.internal.foreign;
import java.lang.foreign.MemorySegment;
import java.lang.foreign.ValueLayout;
import java.nio.ByteBuffer;
import java.util.Objects;
@ -48,7 +47,7 @@ import jdk.internal.vm.annotation.ForceInline;
* using sharper types would require use of type-conversions, which in turn would inhibit some C2 optimizations,
* such as the elimination of store barriers in methods like {@link HeapMemorySegmentImpl#dup(long, long, boolean, MemorySessionImpl)}.
*/
public abstract sealed class HeapMemorySegmentImpl extends AbstractMemorySegmentImpl {
abstract sealed class HeapMemorySegmentImpl extends AbstractMemorySegmentImpl {
private static final Unsafe UNSAFE = Unsafe.getUnsafe();
private static final int BYTE_ARR_BASE = UNSAFE.arrayBaseOffset(byte[].class);
@ -110,13 +109,6 @@ public abstract sealed class HeapMemorySegmentImpl extends AbstractMemorySegment
return (byte[])Objects.requireNonNull(base);
}
public static MemorySegment fromArray(byte[] arr) {
Objects.requireNonNull(arr);
long byteSize = (long)arr.length * Unsafe.ARRAY_BYTE_INDEX_SCALE;
return new OfByte(Unsafe.ARRAY_BYTE_BASE_OFFSET, arr, byteSize, false,
MemorySessionImpl.heapSession(arr));
}
@Override
public long maxAlignMask() {
return MAX_ALIGN_1;
@ -144,13 +136,6 @@ public abstract sealed class HeapMemorySegmentImpl extends AbstractMemorySegment
return (char[])Objects.requireNonNull(base);
}
public static MemorySegment fromArray(char[] arr) {
Objects.requireNonNull(arr);
long byteSize = (long)arr.length * Unsafe.ARRAY_CHAR_INDEX_SCALE;
return new OfChar(Unsafe.ARRAY_CHAR_BASE_OFFSET, arr, byteSize, false,
MemorySessionImpl.heapSession(arr));
}
@Override
public long maxAlignMask() {
return MAX_ALIGN_2;
@ -178,13 +163,6 @@ public abstract sealed class HeapMemorySegmentImpl extends AbstractMemorySegment
return (short[])Objects.requireNonNull(base);
}
public static MemorySegment fromArray(short[] arr) {
Objects.requireNonNull(arr);
long byteSize = (long)arr.length * Unsafe.ARRAY_SHORT_INDEX_SCALE;
return new OfShort(Unsafe.ARRAY_SHORT_BASE_OFFSET, arr, byteSize, false,
MemorySessionImpl.heapSession(arr));
}
@Override
public long maxAlignMask() {
return MAX_ALIGN_2;
@ -212,13 +190,6 @@ public abstract sealed class HeapMemorySegmentImpl extends AbstractMemorySegment
return (int[])Objects.requireNonNull(base);
}
public static MemorySegment fromArray(int[] arr) {
Objects.requireNonNull(arr);
long byteSize = (long)arr.length * Unsafe.ARRAY_INT_INDEX_SCALE;
return new OfInt(Unsafe.ARRAY_INT_BASE_OFFSET, arr, byteSize, false,
MemorySessionImpl.heapSession(arr));
}
@Override
public long maxAlignMask() {
return MAX_ALIGN_4;
@ -246,13 +217,6 @@ public abstract sealed class HeapMemorySegmentImpl extends AbstractMemorySegment
return (long[])Objects.requireNonNull(base);
}
public static MemorySegment fromArray(long[] arr) {
Objects.requireNonNull(arr);
long byteSize = (long)arr.length * Unsafe.ARRAY_LONG_INDEX_SCALE;
return new OfLong(Unsafe.ARRAY_LONG_BASE_OFFSET, arr, byteSize, false,
MemorySessionImpl.heapSession(arr));
}
@Override
public long maxAlignMask() {
return MAX_ALIGN_8;
@ -280,13 +244,6 @@ public abstract sealed class HeapMemorySegmentImpl extends AbstractMemorySegment
return (float[])Objects.requireNonNull(base);
}
public static MemorySegment fromArray(float[] arr) {
Objects.requireNonNull(arr);
long byteSize = (long)arr.length * Unsafe.ARRAY_FLOAT_INDEX_SCALE;
return new OfFloat(Unsafe.ARRAY_FLOAT_BASE_OFFSET, arr, byteSize, false,
MemorySessionImpl.heapSession(arr));
}
@Override
public long maxAlignMask() {
return MAX_ALIGN_4;
@ -314,13 +271,6 @@ public abstract sealed class HeapMemorySegmentImpl extends AbstractMemorySegment
return (double[])Objects.requireNonNull(base);
}
public static MemorySegment fromArray(double[] arr) {
Objects.requireNonNull(arr);
long byteSize = (long)arr.length * Unsafe.ARRAY_DOUBLE_INDEX_SCALE;
return new OfDouble(Unsafe.ARRAY_DOUBLE_BASE_OFFSET, arr, byteSize, false,
MemorySessionImpl.heapSession(arr));
}
@Override
public long maxAlignMask() {
return MAX_ALIGN_8;

@ -35,7 +35,7 @@ import jdk.internal.misc.ScopedMemoryAccess;
* memory mapped segment, such as the file descriptor associated with the mapping. This information is crucial
* in order to correctly reconstruct a byte buffer object from the segment (see {@link #makeByteBuffer()}).
*/
public final class MappedMemorySegmentImpl extends NativeMemorySegmentImpl {
final class MappedMemorySegmentImpl extends NativeMemorySegmentImpl {
private final UnmapperProxy unmapper;

@ -31,20 +31,13 @@ import java.nio.ByteBuffer;
import java.util.Optional;
import jdk.internal.misc.Unsafe;
import jdk.internal.misc.VM;
import jdk.internal.vm.annotation.ForceInline;
/**
* Implementation for native memory segments. A native memory segment is essentially a wrapper around
* a native long address.
*/
public sealed class NativeMemorySegmentImpl extends AbstractMemorySegmentImpl permits MappedMemorySegmentImpl {
private static final Unsafe UNSAFE = Unsafe.getUnsafe();
// The maximum alignment supported by malloc - typically 16 bytes on
// 64-bit platforms and 8 bytes on 32-bit platforms.
private static final long MAX_MALLOC_ALIGN = Unsafe.ADDRESS_SIZE == 4 ? 8 : 16;
sealed class NativeMemorySegmentImpl extends AbstractMemorySegmentImpl permits MappedMemorySegmentImpl {
final long min;
@ -58,17 +51,6 @@ public sealed class NativeMemorySegmentImpl extends AbstractMemorySegmentImpl pe
: min;
}
/**
* This constructor should only be used when initializing {@link MemorySegment#NULL}. Note: because of the memory
* segment class hierarchy, it is possible to end up in a situation where this constructor is called
* when the static fields in this class are not yet initialized.
*/
@ForceInline
public NativeMemorySegmentImpl() {
super(0L, false, new GlobalSession(null));
this.min = 0L;
}
@Override
public long address() {
return min;
@ -110,72 +92,4 @@ public sealed class NativeMemorySegmentImpl extends AbstractMemorySegmentImpl pe
public long maxAlignMask() {
return 0;
}
// factories
public static MemorySegment makeNativeSegmentNoZeroing(long byteSize, long byteAlignment, MemorySessionImpl sessionImpl,
boolean shouldReserve) {
sessionImpl.checkValidState();
if (VM.isDirectMemoryPageAligned()) {
byteAlignment = Math.max(byteAlignment, NIO_ACCESS.pageSize());
}
long alignedSize = Math.max(1L, byteAlignment > MAX_MALLOC_ALIGN ?
byteSize + (byteAlignment - 1) :
byteSize);
if (shouldReserve) {
NIO_ACCESS.reserveMemory(alignedSize, byteSize);
}
long buf = allocateMemoryWrapper(alignedSize);
long alignedBuf = Utils.alignUp(buf, byteAlignment);
AbstractMemorySegmentImpl segment = new NativeMemorySegmentImpl(buf, alignedSize,
false, sessionImpl);
sessionImpl.addOrCleanupIfFail(new MemorySessionImpl.ResourceList.ResourceCleanup() {
@Override
public void cleanup() {
UNSAFE.freeMemory(buf);
if (shouldReserve) {
NIO_ACCESS.unreserveMemory(alignedSize, byteSize);
}
}
});
if (alignedSize != byteSize) {
long delta = alignedBuf - buf;
segment = segment.asSlice(delta, byteSize);
}
return segment;
}
private static long allocateMemoryWrapper(long size) {
try {
return UNSAFE.allocateMemory(size);
} catch (IllegalArgumentException ex) {
throw new OutOfMemoryError();
}
}
// Unsafe native segment factories. These are used by the implementation code, to skip the sanity checks
// associated with MemorySegment::ofAddress.
@ForceInline
public static MemorySegment makeNativeSegmentUnchecked(long min, long byteSize, MemorySessionImpl sessionImpl, Runnable action) {
if (action == null) {
sessionImpl.checkValidState();
} else {
sessionImpl.addCloseAction(action);
}
return new NativeMemorySegmentImpl(min, byteSize, false, sessionImpl);
}
@ForceInline
public static MemorySegment makeNativeSegmentUnchecked(long min, long byteSize, MemorySessionImpl sessionImpl) {
sessionImpl.checkValidState();
return new NativeMemorySegmentImpl(min, byteSize, false, sessionImpl);
}
@ForceInline
public static MemorySegment makeNativeSegmentUnchecked(long min, long byteSize) {
return new NativeMemorySegmentImpl(min, byteSize, false, new GlobalSession(null));
}
}

@ -0,0 +1,211 @@
/*
* Copyright (c) 2023, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 jdk.internal.foreign;
import jdk.internal.access.foreign.UnmapperProxy;
import jdk.internal.foreign.HeapMemorySegmentImpl.OfByte;
import jdk.internal.foreign.HeapMemorySegmentImpl.OfChar;
import jdk.internal.foreign.HeapMemorySegmentImpl.OfDouble;
import jdk.internal.foreign.HeapMemorySegmentImpl.OfFloat;
import jdk.internal.foreign.HeapMemorySegmentImpl.OfInt;
import jdk.internal.foreign.HeapMemorySegmentImpl.OfLong;
import jdk.internal.foreign.HeapMemorySegmentImpl.OfShort;
import jdk.internal.misc.Unsafe;
import jdk.internal.misc.VM;
import jdk.internal.vm.annotation.ForceInline;
import java.lang.foreign.MemorySegment;
import java.util.Objects;
/**
* This class is used to retrieve concrete memory segment implementations, while making sure that classes
* are initialized in the right order (that is, that {@code MemorySegment} is always initialized first).
* See {@link SegmentFactories#ensureInitialized()}.
*/
public class SegmentFactories {
// The maximum alignment supported by malloc - typically 16 bytes on
// 64-bit platforms and 8 bytes on 32-bit platforms.
private static final long MAX_MALLOC_ALIGN = Unsafe.ADDRESS_SIZE == 4 ? 8 : 16;
private static final Unsafe UNSAFE = Unsafe.getUnsafe();
// Unsafe native segment factories. These are used by the implementation code, to skip the sanity checks
// associated with MemorySegment::ofAddress.
@ForceInline
public static MemorySegment makeNativeSegmentUnchecked(long min, long byteSize, MemorySessionImpl sessionImpl, Runnable action) {
ensureInitialized();
if (action == null) {
sessionImpl.checkValidState();
} else {
sessionImpl.addCloseAction(action);
}
return new NativeMemorySegmentImpl(min, byteSize, false, sessionImpl);
}
@ForceInline
public static MemorySegment makeNativeSegmentUnchecked(long min, long byteSize, MemorySessionImpl sessionImpl) {
ensureInitialized();
sessionImpl.checkValidState();
return new NativeMemorySegmentImpl(min, byteSize, false, sessionImpl);
}
@ForceInline
public static MemorySegment makeNativeSegmentUnchecked(long min, long byteSize) {
ensureInitialized();
return new NativeMemorySegmentImpl(min, byteSize, false, new GlobalSession(null));
}
public static MemorySegment fromArray(byte[] arr) {
ensureInitialized();
Objects.requireNonNull(arr);
long byteSize = (long)arr.length * Unsafe.ARRAY_BYTE_INDEX_SCALE;
return new OfByte(Unsafe.ARRAY_BYTE_BASE_OFFSET, arr, byteSize, false,
MemorySessionImpl.heapSession(arr));
}
public static MemorySegment fromArray(short[] arr) {
ensureInitialized();
Objects.requireNonNull(arr);
long byteSize = (long)arr.length * Unsafe.ARRAY_SHORT_INDEX_SCALE;
return new OfShort(Unsafe.ARRAY_SHORT_BASE_OFFSET, arr, byteSize, false,
MemorySessionImpl.heapSession(arr));
}
public static MemorySegment fromArray(int[] arr) {
ensureInitialized();
Objects.requireNonNull(arr);
long byteSize = (long)arr.length * Unsafe.ARRAY_INT_INDEX_SCALE;
return new OfInt(Unsafe.ARRAY_INT_BASE_OFFSET, arr, byteSize, false,
MemorySessionImpl.heapSession(arr));
}
public static MemorySegment fromArray(char[] arr) {
ensureInitialized();
Objects.requireNonNull(arr);
long byteSize = (long)arr.length * Unsafe.ARRAY_CHAR_INDEX_SCALE;
return new OfChar(Unsafe.ARRAY_CHAR_BASE_OFFSET, arr, byteSize, false,
MemorySessionImpl.heapSession(arr));
}
public static MemorySegment fromArray(float[] arr) {
ensureInitialized();
Objects.requireNonNull(arr);
long byteSize = (long)arr.length * Unsafe.ARRAY_FLOAT_INDEX_SCALE;
return new OfFloat(Unsafe.ARRAY_FLOAT_BASE_OFFSET, arr, byteSize, false,
MemorySessionImpl.heapSession(arr));
}
public static MemorySegment fromArray(double[] arr) {
ensureInitialized();
Objects.requireNonNull(arr);
long byteSize = (long)arr.length * Unsafe.ARRAY_DOUBLE_INDEX_SCALE;
return new OfDouble(Unsafe.ARRAY_DOUBLE_BASE_OFFSET, arr, byteSize, false,
MemorySessionImpl.heapSession(arr));
}
public static MemorySegment fromArray(long[] arr) {
ensureInitialized();
Objects.requireNonNull(arr);
long byteSize = (long)arr.length * Unsafe.ARRAY_LONG_INDEX_SCALE;
return new OfLong(Unsafe.ARRAY_LONG_BASE_OFFSET, arr, byteSize, false,
MemorySessionImpl.heapSession(arr));
}
public static MemorySegment allocateSegment(long byteSize, long byteAlignment, MemorySessionImpl sessionImpl,
boolean shouldReserve) {
ensureInitialized();
sessionImpl.checkValidState();
if (VM.isDirectMemoryPageAligned()) {
byteAlignment = Math.max(byteAlignment, AbstractMemorySegmentImpl.NIO_ACCESS.pageSize());
}
long alignedSize = Math.max(1L, byteAlignment > MAX_MALLOC_ALIGN ?
byteSize + (byteAlignment - 1) :
byteSize);
if (shouldReserve) {
AbstractMemorySegmentImpl.NIO_ACCESS.reserveMemory(alignedSize, byteSize);
}
long buf = allocateMemoryWrapper(alignedSize);
long alignedBuf = Utils.alignUp(buf, byteAlignment);
AbstractMemorySegmentImpl segment = new NativeMemorySegmentImpl(buf, alignedSize,
false, sessionImpl);
sessionImpl.addOrCleanupIfFail(new MemorySessionImpl.ResourceList.ResourceCleanup() {
@Override
public void cleanup() {
UNSAFE.freeMemory(buf);
if (shouldReserve) {
AbstractMemorySegmentImpl.NIO_ACCESS.unreserveMemory(alignedSize, byteSize);
}
}
});
if (alignedSize != byteSize) {
long delta = alignedBuf - buf;
segment = segment.asSlice(delta, byteSize);
}
return segment;
}
private static long allocateMemoryWrapper(long size) {
try {
return UNSAFE.allocateMemory(size);
} catch (IllegalArgumentException ex) {
throw new OutOfMemoryError();
}
}
public static MemorySegment mapSegment(long size, UnmapperProxy unmapper, boolean readOnly, MemorySessionImpl sessionImpl) {
ensureInitialized();
if (unmapper != null) {
AbstractMemorySegmentImpl segment =
new MappedMemorySegmentImpl(unmapper.address(), unmapper, size,
readOnly, sessionImpl);
MemorySessionImpl.ResourceList.ResourceCleanup resource =
new MemorySessionImpl.ResourceList.ResourceCleanup() {
@Override
public void cleanup() {
unmapper.unmap();
}
};
sessionImpl.addOrCleanupIfFail(resource);
return segment;
} else {
return new MappedMemorySegmentImpl(0, null, 0, readOnly, sessionImpl);
}
}
// The method below needs to be called before any concrete subclass of MemorySegment
// is instantiated. This is to make sure that we cannot have an initialization deadlock
// where one thread attempts to initialize e.g. MemorySegment (and then NativeMemorySegmentImpl, via
// the MemorySegment.NULL field) while another thread is attempting to initialize
// NativeMemorySegmentImpl (and then MemorySegment, the super-interface).
@ForceInline
private static void ensureInitialized() {
MemorySegment segment = MemorySegment.NULL;
}
}

@ -148,7 +148,7 @@ public final class Utils {
if (!isAligned(addr, align)) {
throw new IllegalArgumentException("Invalid alignment constraint for address: " + toHexString(addr));
}
return NativeMemorySegmentImpl.makeNativeSegmentUnchecked(addr, size);
return SegmentFactories.makeNativeSegmentUnchecked(addr, size);
}
@ForceInline
@ -156,7 +156,7 @@ public final class Utils {
if (!isAligned(addr, align)) {
throw new IllegalArgumentException("Invalid alignment constraint for address: " + toHexString(addr));
}
return NativeMemorySegmentImpl.makeNativeSegmentUnchecked(addr, size, scope);
return SegmentFactories.makeNativeSegmentUnchecked(addr, size, scope);
}
@ForceInline

@ -50,9 +50,8 @@ import java.util.Objects;
import jdk.internal.access.JavaIOFileDescriptorAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.foreign.AbstractMemorySegmentImpl;
import jdk.internal.foreign.MappedMemorySegmentImpl;
import jdk.internal.foreign.MemorySessionImpl;
import jdk.internal.foreign.SegmentFactories;
import jdk.internal.misc.Blocker;
import jdk.internal.misc.ExtendedMapMode;
import jdk.internal.misc.Unsafe;
@ -1335,22 +1334,7 @@ public class FileChannelImpl
if (mode == MapMode.READ_ONLY) {
readOnly = true;
}
if (unmapper != null) {
AbstractMemorySegmentImpl segment =
new MappedMemorySegmentImpl(unmapper.address(), unmapper, size,
readOnly, sessionImpl);
MemorySessionImpl.ResourceList.ResourceCleanup resource =
new MemorySessionImpl.ResourceList.ResourceCleanup() {
@Override
public void cleanup() {
unmapper.unmap();
}
};
sessionImpl.addOrCleanupIfFail(resource);
return segment;
} else {
return new MappedMemorySegmentImpl(0, null, 0, readOnly, sessionImpl);
}
return SegmentFactories.mapSegment(size, unmapper, readOnly, sessionImpl);
}
private Unmapper mapInternal(MapMode mode, long position, long size, int prot, boolean isSync)

@ -69,9 +69,6 @@ import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Stream;
import jdk.internal.foreign.HeapMemorySegmentImpl;
import jdk.internal.foreign.MappedMemorySegmentImpl;
import jdk.internal.foreign.NativeMemorySegmentImpl;
import org.testng.SkipException;
import org.testng.annotations.*;
import sun.nio.ch.DirectBuffer;
@ -987,9 +984,9 @@ public class TestByteBuffer {
@DataProvider(name = "bufferSources")
public static Object[][] bufferSources() {
Predicate<MemorySegment> heapTest = segment -> segment instanceof HeapMemorySegmentImpl;
Predicate<MemorySegment> nativeTest = segment -> segment instanceof NativeMemorySegmentImpl;
Predicate<MemorySegment> mappedTest = segment -> segment instanceof MappedMemorySegmentImpl;
Predicate<MemorySegment> heapTest = segment -> !segment.isNative() && !segment.isMapped();
Predicate<MemorySegment> nativeTest = segment -> segment.isNative() && !segment.isMapped();
Predicate<MemorySegment> mappedTest = segment -> segment.isNative() && segment.isMapped();
try (FileChannel channel = FileChannel.open(tempPath, StandardOpenOption.READ, StandardOpenOption.WRITE)) {
return new Object[][]{
{ ByteBuffer.wrap(new byte[256]), heapTest },
@ -1001,7 +998,7 @@ public class TestByteBuffer {
{ ByteBuffer.allocate(256).asReadOnlyBuffer(), heapTest },
{ ByteBuffer.allocateDirect(256).asReadOnlyBuffer(), nativeTest },
{ channel.map(FileChannel.MapMode.READ_WRITE, 0L, 256).asReadOnlyBuffer(),
nativeTest /* this seems to be an existing bug in the BB implementation */ }
mappedTest }
};
} catch (IOException ex) {
throw new ExceptionInInitializerError(ex);

@ -0,0 +1,101 @@
/*
* Copyright (c) 2023, 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 id=Arena_allocateFrom
* @run main/othervm/timeout=5 --enable-native-access=ALL-UNNAMED -Xlog:class+init TestDeadlock Arena
*/
/*
* @test id=FileChannel_map
* @run main/othervm/timeout=5 --enable-native-access=ALL-UNNAMED -Xlog:class+init TestDeadlock FileChannel
*/
import java.lang.foreign.*;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.concurrent.CountDownLatch;
public class TestDeadlock {
public static void main(String[] args) throws Throwable {
CountDownLatch latch = new CountDownLatch(2);
Runnable tester = switch (args[0]) {
case "Arena" -> () -> {
Arena arena = Arena.global();
arena.scope(); // init ArenaImpl
ValueLayout.JAVA_INT.byteSize(); // init ValueLayout (and impls)
latch.countDown();
try {
latch.await();
} catch(InterruptedException e) {
throw new RuntimeException(e);
}
// Access ArenaImpl -> NativeMemorySegmentImpl -> MemorySegment
arena.allocateFrom(ValueLayout.JAVA_INT, 42);
};
case "FileChannel" -> () -> {
try {
Arena arena = Arena.global();
Path p = Files.createFile(Path.of("test.out"));
try (FileChannel channel = FileChannel.open(p, StandardOpenOption.READ, StandardOpenOption.WRITE)) {
channel.map(FileChannel.MapMode.READ_WRITE, 0, 4); // create MappedByteBuffer to initialize other things
latch.countDown();
latch.await();
// Access MappedMemorySegmentImpl -> MemorySegment
channel.map(FileChannel.MapMode.READ_WRITE, 0, 4, arena);
}
} catch(InterruptedException | IOException e) {
throw new RuntimeException(e);
}
};
default -> throw new IllegalArgumentException("Unknown test selection: " + args[0]);
};
Thread t1 = Thread.ofPlatform().start(tester);
Thread t2 = Thread.ofPlatform().start(() -> {
latch.countDown();
try {
latch.await();
} catch(InterruptedException e) {
throw new RuntimeException(e);
}
// Access MemorySegment -> NativeMemorySegmentImpl
MemorySegment.ofAddress(42);
});
// wait for potential deadlock
t1.join();
t2.join();
// all good
}
}

@ -30,7 +30,6 @@
import java.lang.foreign.*;
import jdk.internal.foreign.NativeMemorySegmentImpl;
import org.testng.annotations.*;
import java.lang.foreign.Arena;
@ -239,7 +238,7 @@ public class TestSegmentAllocators {
assertThrows(UnsupportedOperationException.class, segment::isLoaded);
assertThrows(UnsupportedOperationException.class, segment::force);
assertFalse(segment.isMapped());
assertEquals(segment.isNative(), segment instanceof NativeMemorySegmentImpl);
assertTrue(segment.isNative());
}
}
}