--- old/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemorySegment.java 2020-04-30 16:03:59.000000000 +0100 +++ new/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemorySegment.java 2020-04-30 16:03:59.000000000 +0100 @@ -177,7 +177,8 @@ * The returned spliterator splits the segment according to the specified sequence layout; that is, * if the supplied layout is a sequence layout whose element count is {@code N}, then calling {@link Spliterator#trySplit()} * will result in a spliterator serving approximatively {@code N/2} elements (depending on whether N is even or not). - * As such, splitting is possible as long as {@code N >= 2}. + * As such, splitting is possible as long as {@code N >= 2}. The spliterator returns segments that feature the same + * access modes as the given segment less the {@link #CLOSE} access mode. *

* The returned spliterator effectively allows to slice a segment into disjoint sub-segments, which can then * be processed in parallel by multiple threads (if the access mode {@link #ACQUIRE} is set). @@ -320,6 +321,10 @@ * buffer. The segment starts relative to the buffer's position (inclusive) * and ends relative to the buffer's limit (exclusive). *

+ * The segment will feature all access modes, unless the given + * buffer is {@linkplain ByteBuffer#isReadOnly() read-only} in which case the segment will + * not feature the {@link #WRITE} access mode. + *

* The resulting memory segment keeps a reference to the backing buffer, to ensure it remains reachable * for the life-time of the segment. * @@ -334,7 +339,7 @@ * Creates a new array memory segment that models the memory associated with a given heap-allocated byte array. *

* The resulting memory segment keeps a reference to the backing array, to ensure it remains reachable - * for the life-time of the segment. + * for the life-time of the segment. The segment will feature all access modes. * * @param arr the primitive array backing the array memory segment. * @return a new array memory segment. @@ -347,7 +352,7 @@ * Creates a new array memory segment that models the memory associated with a given heap-allocated char array. *

* The resulting memory segment keeps a reference to the backing array, to ensure it remains reachable - * for the life-time of the segment. + * for the life-time of the segment. The segment will feature all access modes. * * @param arr the primitive array backing the array memory segment. * @return a new array memory segment. @@ -360,7 +365,7 @@ * Creates a new array memory segment that models the memory associated with a given heap-allocated short array. *

* The resulting memory segment keeps a reference to the backing array, to ensure it remains reachable - * for the life-time of the segment. + * for the life-time of the segment. The segment will feature all access modes. * * @param arr the primitive array backing the array memory segment. * @return a new array memory segment. @@ -373,7 +378,7 @@ * Creates a new array memory segment that models the memory associated with a given heap-allocated int array. *

* The resulting memory segment keeps a reference to the backing array, to ensure it remains reachable - * for the life-time of the segment. + * for the life-time of the segment. The segment will feature all access modes. * * @param arr the primitive array backing the array memory segment. * @return a new array memory segment. @@ -386,7 +391,7 @@ * Creates a new array memory segment that models the memory associated with a given heap-allocated float array. *

* The resulting memory segment keeps a reference to the backing array, to ensure it remains reachable - * for the life-time of the segment. + * for the life-time of the segment. The segment will feature all access modes. * * @param arr the primitive array backing the array memory segment. * @return a new array memory segment. @@ -399,7 +404,7 @@ * Creates a new array memory segment that models the memory associated with a given heap-allocated long array. *

* The resulting memory segment keeps a reference to the backing array, to ensure it remains reachable - * for the life-time of the segment. + * for the life-time of the segment. The segment will feature all access modes. * * @param arr the primitive array backing the array memory segment. * @return a new array memory segment. @@ -412,7 +417,7 @@ * Creates a new array memory segment that models the memory associated with a given heap-allocated double array. *

* The resulting memory segment keeps a reference to the backing array, to ensure it remains reachable - * for the life-time of the segment. + * for the life-time of the segment. The segment will feature all access modes. * * @param arr the primitive array backing the array memory segment. * @return a new array memory segment. @@ -463,6 +468,10 @@ /** * Creates a new mapped memory segment that models a memory-mapped region of a file from a given path. + *

+ * The segment will feature all access modes, unless the given mapping mode + * is {@linkplain FileChannel.MapMode#READ_ONLY READ_ONLY}, in which case the segment will not feature + * the {@link #WRITE} access mode. * * @implNote When obtaining a mapped segment from a newly created file, the initialization state of the contents of the block * of mapped memory associated with the returned mapped memory segment is unspecified and should not be relied upon. @@ -482,7 +491,7 @@ /** * Creates a new native memory segment that models a newly allocated block of off-heap memory with given size and - * alignment constraint (in bytes). + * alignment constraint (in bytes). The segment will feature all access modes. * * @implNote The block of off-heap memory associated with the returned native memory segment is initialized to zero. * Moreover, a client is responsible to call the {@link MemorySegment#close()} on a native memory segment, @@ -512,7 +521,7 @@ * bounds, and can therefore be closed; closing such a segment can optionally result in calling an user-provided cleanup * action. This method can be very useful when interacting with custom native memory sources (e.g. custom allocators, * GPU memory, etc.), where an address to some underlying memory region is typically obtained from native code - * (often as a plain {@code long} value). + * (often as a plain {@code long} value). The segment will feature all access modes. *

* This method is restricted. Restricted method are unsafe, and, if used incorrectly, their use might crash * the JVM crash or, worse, silently result in memory corruption. Thus, clients should refrain from depending on --- old/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/MappedMemorySegmentImpl.java 2020-04-30 16:04:02.000000000 +0100 +++ new/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/MappedMemorySegmentImpl.java 2020-04-30 16:04:02.000000000 +0100 @@ -104,8 +104,12 @@ try (FileChannelImpl channelImpl = (FileChannelImpl)FileChannel.open(path, openOptions(mapMode))) { UnmapperProxy unmapperProxy = channelImpl.mapInternal(mapMode, 0L, bytesSize); MemoryScope scope = new MemoryScope(null, unmapperProxy::unmap); + int modes = defaultAccessModes(bytesSize); + if (mapMode == FileChannel.MapMode.READ_ONLY) { + modes &= ~WRITE; + } return new MappedMemorySegmentImpl(unmapperProxy.address(), unmapperProxy, bytesSize, - defaultAccessModes(bytesSize), Thread.currentThread(), scope); + modes, Thread.currentThread(), scope); } } --- old/test/jdk/java/foreign/TestByteBuffer.java 2020-04-30 16:04:05.000000000 +0100 +++ new/test/jdk/java/foreign/TestByteBuffer.java 2020-04-30 16:04:05.000000000 +0100 @@ -78,7 +78,7 @@ import org.testng.SkipException; import org.testng.annotations.*; import sun.nio.ch.DirectBuffer; - +import static jdk.incubator.foreign.MemorySegment.*; import static org.testng.Assert.*; public class TestByteBuffer { @@ -236,6 +236,21 @@ } } + static final int ALL_ACCESS_MODES = READ | WRITE | CLOSE | ACQUIRE | HANDOFF; + + @Test + public void testDefaultAccessModesMappedSegment() throws Throwable { + try (MappedMemorySegment segment = MemorySegment.mapFromPath(tempPath, 8, FileChannel.MapMode.READ_WRITE)) { + assertTrue(segment.hasAccessModes(ALL_ACCESS_MODES)); + assertEquals(segment.accessModes(), ALL_ACCESS_MODES); + } + + try (MappedMemorySegment segment = MemorySegment.mapFromPath(tempPath, 8, FileChannel.MapMode.READ_ONLY)) { + assertTrue(segment.hasAccessModes(ALL_ACCESS_MODES & ~WRITE)); + assertEquals(segment.accessModes(), ALL_ACCESS_MODES& ~WRITE); + } + } + @Test public void testMappedSegment() throws Throwable { File f = new File("test2.out"); @@ -446,6 +461,21 @@ } } + @Test + public void testDefaultAccessModesOfBuffer() { + ByteBuffer rwBuffer = ByteBuffer.wrap(new byte[4]); + try (MemorySegment segment = MemorySegment.ofByteBuffer(rwBuffer)) { + assertTrue(segment.hasAccessModes(ALL_ACCESS_MODES)); + assertEquals(segment.accessModes(), ALL_ACCESS_MODES); + } + + ByteBuffer roBuffer = rwBuffer.asReadOnlyBuffer(); + try (MemorySegment segment = MemorySegment.ofByteBuffer(roBuffer)) { + assertTrue(segment.hasAccessModes(ALL_ACCESS_MODES & ~WRITE)); + assertEquals(segment.accessModes(), ALL_ACCESS_MODES & ~WRITE); + } + } + @Test(dataProvider="bufferSources") public void testBufferToSegment(ByteBuffer bb, Predicate segmentChecker) { MemorySegment segment = MemorySegment.ofByteBuffer(bb); --- old/test/jdk/java/foreign/TestNative.java 2020-04-30 16:04:08.000000000 +0100 +++ new/test/jdk/java/foreign/TestNative.java 2020-04-30 16:04:08.000000000 +0100 @@ -53,7 +53,7 @@ import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.function.Function; - +import static jdk.incubator.foreign.MemorySegment.*; import static org.testng.Assert.*; public class TestNative { @@ -172,6 +172,19 @@ } } + static final int ALL_ACCESS_MODES = READ | WRITE | CLOSE | ACQUIRE | HANDOFF; + + @Test + public void testDefaultAccessModes() { + MemoryAddress addr = MemoryAddress.ofLong(allocate(12)); + MemorySegment mallocSegment = MemorySegment.ofNativeRestricted(addr, 12, null, + () -> free(addr.toRawLongValue()), null); + try (MemorySegment segment = mallocSegment) { + assertTrue(segment.hasAccessModes(ALL_ACCESS_MODES)); + assertEquals(segment.accessModes(), ALL_ACCESS_MODES); + } + } + @Test public void testMallocSegment() { MemoryAddress addr = MemoryAddress.ofLong(allocate(12)); --- old/test/jdk/java/foreign/TestSegments.java 2020-04-30 16:04:10.000000000 +0100 +++ new/test/jdk/java/foreign/TestSegments.java 2020-04-30 16:04:10.000000000 +0100 @@ -43,8 +43,9 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import java.util.function.LongFunction; +import java.util.function.Supplier; import java.util.stream.Stream; - +import static jdk.incubator.foreign.MemorySegment.*; import static org.testng.Assert.*; public class TestSegments { @@ -144,6 +145,33 @@ } } + static final int ALL_ACCESS_MODES = READ | WRITE | CLOSE | ACQUIRE | HANDOFF; + + @DataProvider(name = "segmentFactories") + public Object[][] segmentFactories() { + List> l = List.of( + () -> MemorySegment.ofArray(new byte[1]), + () -> MemorySegment.ofArray(new char[1]), + () -> MemorySegment.ofArray(new double[1]), + () -> MemorySegment.ofArray(new float[1]), + () -> MemorySegment.ofArray(new int[1]), + () -> MemorySegment.ofArray(new long[1]), + () -> MemorySegment.ofArray(new short[1]), + () -> MemorySegment.ofArray(new int[1]), + () -> MemorySegment.allocateNative(1), + () -> MemorySegment.allocateNative(1, 2), + () -> MemorySegment.allocateNative(MemoryLayout.ofValueBits(8, ByteOrder.LITTLE_ENDIAN)) + ); + return l.stream().map(s -> new Object[] { s }).toArray(Object[][]::new); + } + @Test(dataProvider = "segmentFactories") + public void testAccessModesOfFactories(Supplier memorySegmentSupplier) { + try (MemorySegment segment = memorySegmentSupplier.get()) { + assertTrue(segment.hasAccessModes(ALL_ACCESS_MODES)); + assertEquals(segment.accessModes(), ALL_ACCESS_MODES); + } + } + @Test(dataProvider = "accessModes") public void testAccessModes(int accessModes) { int[] arr = new int[1]; --- old/test/jdk/java/foreign/TestSpliterator.java 2020-04-30 16:04:13.000000000 +0100 +++ new/test/jdk/java/foreign/TestSpliterator.java 2020-04-30 16:04:13.000000000 +0100 @@ -35,14 +35,18 @@ import java.lang.invoke.VarHandle; import java.util.LinkedList; import java.util.List; +import java.util.Map; import java.util.Spliterator; import java.util.concurrent.CountedCompleter; import java.util.concurrent.RecursiveTask; import java.util.concurrent.atomic.AtomicLong; +import java.util.function.Consumer; +import java.util.function.Supplier; import java.util.stream.LongStream; import java.util.stream.StreamSupport; import org.testng.annotations.*; +import static jdk.incubator.foreign.MemorySegment.*; import static org.testng.Assert.*; public class TestSpliterator { @@ -90,7 +94,7 @@ //check that a segment w/o ACQUIRE access mode can still be used from same thread AtomicLong spliteratorSum = new AtomicLong(); - MemorySegment.spliterator(segment.withAccessModes(MemorySegment.READ), layout) + spliterator(segment.withAccessModes(MemorySegment.READ), layout) .forEachRemaining(s -> spliteratorSum.addAndGet(sumSingle(0L, s))); assertEquals(spliteratorSum.get(), expected); } @@ -200,4 +204,54 @@ { 10000, 10000 }, }; } + + static final int ALL_ACCESS_MODES = READ | WRITE | CLOSE | ACQUIRE | HANDOFF; + + @DataProvider(name = "accessScenarios") + public Object[][] accessScenarios() { + SequenceLayout layout = MemoryLayout.ofSequence(16, MemoryLayouts.JAVA_INT); + var mallocSegment = MemorySegment.allocateNative(layout); + + Map>,Integer> l = Map.of( + () -> spliterator(mallocSegment.withAccessModes(ALL_ACCESS_MODES), layout), ALL_ACCESS_MODES, + () -> spliterator(mallocSegment.withAccessModes(0), layout), 0, + () -> spliterator(mallocSegment.withAccessModes(READ), layout), READ, + () -> spliterator(mallocSegment.withAccessModes(CLOSE), layout), 0, + () -> spliterator(mallocSegment.withAccessModes(READ|WRITE), layout), READ|WRITE, + () -> spliterator(mallocSegment.withAccessModes(READ|WRITE|ACQUIRE), layout), READ|WRITE|ACQUIRE, + () -> spliterator(mallocSegment.withAccessModes(READ|WRITE|ACQUIRE|HANDOFF), layout), READ|WRITE|ACQUIRE|HANDOFF + + ); + return l.entrySet().stream().map(e -> new Object[] { e.getKey(), e.getValue() }).toArray(Object[][]::new); + } + + static Consumer assertAccessModes(int accessModes) { + return segment -> { + assertTrue(segment.hasAccessModes(accessModes & ~CLOSE)); + assertEquals(segment.accessModes(), accessModes & ~CLOSE); + }; + } + + @Test(dataProvider = "accessScenarios") + public void testAccessModes(Supplier> spliteratorSupplier, + int expectedAccessModes) { + Spliterator spliterator = spliteratorSupplier.get(); + spliterator.forEachRemaining(assertAccessModes(expectedAccessModes)); + + spliterator = spliteratorSupplier.get(); + do { } while (spliterator.tryAdvance(assertAccessModes(expectedAccessModes))); + + splitOrConsume(spliteratorSupplier.get(), assertAccessModes(expectedAccessModes)); + } + + static void splitOrConsume(Spliterator spliterator, + Consumer consumer) { + var s1 = spliterator.trySplit(); + if (s1 != null) { + splitOrConsume(s1, consumer); + splitOrConsume(spliterator, consumer); + } else { + spliterator.forEachRemaining(consumer); + } + } }