8317514: Ensure MemorySegment is initialized before touching NativeMemorySegmentImpl
Co-authored-by: Jorn Vernee <jvernee@openjdk.org> Reviewed-by: jvernee
This commit is contained in:
parent
9622de2aa8
commit
7cb2e6d65b
src/java.base/share/classes
java/lang/foreign
jdk/internal/foreign
AbstractMemorySegmentImpl.javaArenaImpl.javaHeapMemorySegmentImpl.javaMappedMemorySegmentImpl.javaNativeMemorySegmentImpl.javaSegmentFactories.javaUtils.java
sun/nio/ch
test/jdk/java/foreign
@ -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);
|
||||
|
101
test/jdk/java/foreign/TestDeadlock.java
Normal file
101
test/jdk/java/foreign/TestDeadlock.java
Normal file
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user