diff --git a/src/java.base/share/classes/java/lang/foreign/MemorySegment.java b/src/java.base/share/classes/java/lang/foreign/MemorySegment.java index 46fe776d636..2bfdb4ec56e 100644 --- a/src/java.base/share/classes/java/lang/foreign/MemorySegment.java +++ b/src/java.base/share/classes/java/lang/foreign/MemorySegment.java @@ -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); } /** diff --git a/src/java.base/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java b/src/java.base/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java index 6e84f7f4b9e..63132f7f89c 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java +++ b/src/java.base/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java @@ -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); } diff --git a/src/java.base/share/classes/jdk/internal/foreign/ArenaImpl.java b/src/java.base/share/classes/jdk/internal/foreign/ArenaImpl.java index 9d1baecc8b9..027aed5f8cc 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/ArenaImpl.java +++ b/src/java.base/share/classes/jdk/internal/foreign/ArenaImpl.java @@ -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 diff --git a/src/java.base/share/classes/jdk/internal/foreign/HeapMemorySegmentImpl.java b/src/java.base/share/classes/jdk/internal/foreign/HeapMemorySegmentImpl.java index e6b0ab7ab8d..dd6a6e49edd 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/HeapMemorySegmentImpl.java +++ b/src/java.base/share/classes/jdk/internal/foreign/HeapMemorySegmentImpl.java @@ -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; diff --git a/src/java.base/share/classes/jdk/internal/foreign/MappedMemorySegmentImpl.java b/src/java.base/share/classes/jdk/internal/foreign/MappedMemorySegmentImpl.java index 0c9482d8d36..dce223c4683 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/MappedMemorySegmentImpl.java +++ b/src/java.base/share/classes/jdk/internal/foreign/MappedMemorySegmentImpl.java @@ -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; diff --git a/src/java.base/share/classes/jdk/internal/foreign/NativeMemorySegmentImpl.java b/src/java.base/share/classes/jdk/internal/foreign/NativeMemorySegmentImpl.java index 6575388b025..5339d3fb8b7 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/NativeMemorySegmentImpl.java +++ b/src/java.base/share/classes/jdk/internal/foreign/NativeMemorySegmentImpl.java @@ -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)); - } } diff --git a/src/java.base/share/classes/jdk/internal/foreign/SegmentFactories.java b/src/java.base/share/classes/jdk/internal/foreign/SegmentFactories.java new file mode 100644 index 00000000000..c4f1d5a8a0f --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/foreign/SegmentFactories.java @@ -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; + } +} diff --git a/src/java.base/share/classes/jdk/internal/foreign/Utils.java b/src/java.base/share/classes/jdk/internal/foreign/Utils.java index 7bbfc705b80..3809aedba80 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/Utils.java +++ b/src/java.base/share/classes/jdk/internal/foreign/Utils.java @@ -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 diff --git a/src/java.base/share/classes/sun/nio/ch/FileChannelImpl.java b/src/java.base/share/classes/sun/nio/ch/FileChannelImpl.java index 0cbcdf7c94a..f53508859ae 100644 --- a/src/java.base/share/classes/sun/nio/ch/FileChannelImpl.java +++ b/src/java.base/share/classes/sun/nio/ch/FileChannelImpl.java @@ -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) diff --git a/test/jdk/java/foreign/TestByteBuffer.java b/test/jdk/java/foreign/TestByteBuffer.java index 1c920239f77..77b3dd3b105 100644 --- a/test/jdk/java/foreign/TestByteBuffer.java +++ b/test/jdk/java/foreign/TestByteBuffer.java @@ -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 heapTest = segment -> segment instanceof HeapMemorySegmentImpl; - Predicate nativeTest = segment -> segment instanceof NativeMemorySegmentImpl; - Predicate mappedTest = segment -> segment instanceof MappedMemorySegmentImpl; + Predicate heapTest = segment -> !segment.isNative() && !segment.isMapped(); + Predicate nativeTest = segment -> segment.isNative() && !segment.isMapped(); + Predicate 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); diff --git a/test/jdk/java/foreign/TestDeadlock.java b/test/jdk/java/foreign/TestDeadlock.java new file mode 100644 index 00000000000..9144fad3830 --- /dev/null +++ b/test/jdk/java/foreign/TestDeadlock.java @@ -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 + } +} diff --git a/test/jdk/java/foreign/TestSegmentAllocators.java b/test/jdk/java/foreign/TestSegmentAllocators.java index c137eb20371..48e09f647c9 100644 --- a/test/jdk/java/foreign/TestSegmentAllocators.java +++ b/test/jdk/java/foreign/TestSegmentAllocators.java @@ -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()); } } }