changeset 59920:51e4e7f4cbba

8248011: Improve javadoc of Foreign Memory Access API Reviewed-by: psandoz
author mcimadamore
date Tue, 23 Jun 2020 11:49:48 +0100
parents c773c2db5f9c
children 5e2d63e0ce0d
files src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryAddress.java src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryLayout.java src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemorySegment.java src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/package-info.java src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/MemoryAddressImpl.java
diffstat 5 files changed, 111 insertions(+), 107 deletions(-) [+]
line wrap: on
line diff
--- a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryAddress.java	Tue Jun 23 10:09:26 2020 +0100
+++ b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryAddress.java	Tue Jun 23 11:49:48 2020 +0100
@@ -52,7 +52,7 @@
  * explicitly permitted types.
  *
  * @implSpec
- * Implementations of this interface are immutable and thread-safe.
+ * Implementations of this interface are immutable, thread-safe and <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>.
  */
 public interface MemoryAddress {
     /**
--- a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryLayout.java	Tue Jun 23 10:09:26 2020 +0100
+++ b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryLayout.java	Tue Jun 23 11:49:48 2020 +0100
@@ -54,6 +54,27 @@
  * element layout (see {@link SequenceLayout}); a <em>group layout</em> denotes an aggregation of (typically) heterogeneous
  * member layouts (see {@link GroupLayout}).
  * <p>
+ * For instance, consider the following struct declaration in C:
+ *
+ * <blockquote><pre>{@code
+ typedef struct {
+     char kind;
+     int value;
+ } TaggedValues[5];
+ * }</pre></blockquote>
+ *
+ * The above declaration can be modelled using a layout object, as follows:
+ *
+ * <blockquote><pre>{@code
+SequenceLayout taggedValues = MemoryLayout.ofSequence(5,
+    MemoryLayout.ofStruct(
+        MemoryLayout.ofValueBits(8, ByteOrder.NATIVE_ORDER).withName("kind"),
+        MemoryLayout.ofPaddingBits(24),
+        MemoryLayout.ofValueBits(32, ByteOrder.NATIVE_ORDER).withName("value")
+    )
+).withName("TaggedValues");
+ * }</pre></blockquote>
+ * <p>
  * All implementations of this interface must be <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>;
  * use of identity-sensitive operations (including reference equality ({@code ==}), identity hash code, or synchronization) on
  * instances of {@code MemoryLayout} may have unpredictable results and should be avoided. The {@code equals} method should
@@ -103,53 +124,30 @@
  * another layout (see {@link MemoryLayout#map(UnaryOperator, PathElement...)}).
  * <p>
  * Such <em>layout paths</em> can be constructed programmatically using the methods in this class.
- * For instance, given a layout constructed as follows:
+ * For instance, given the {@code taggedValues} layout instance constructed as above, we can obtain the offset,
+ * in bits, of the member layout named <code>value</code> in the <em>first</em> sequence element, as follows:
  * <blockquote><pre>{@code
-SequenceLayout seq = MemoryLayout.ofSequence(5,
-    MemoryLayout.ofStruct(
-        MemoryLayout.ofPaddingBits(32),
-        MemoryLayout.ofValueBits(32, ByteOrder.BIG_ENDIAN).withName("value")
-));
- * }</pre></blockquote>
- *
- * We can obtain the offset, in bits, of the member layout named <code>value</code> from <code>seq</code>, as follows:
- * <blockquote><pre>{@code
-long valueOffset = seq.bitOffset(PathElement.sequenceElement(), PathElement.groupElement("value"));
+long valueOffset = taggedValues.bitOffset(PathElement.sequenceElement(0),
+                                          PathElement.groupElement("value")); // yields 32
  * }</pre></blockquote>
  *
  * Similarly, we can select the member layout named {@code value}, as follows:
  * <blockquote><pre>{@code
-MemoryLayout value = seq.select(PathElement.sequenceElement(), PathElement.groupElement("value"));
+MemoryLayout value = taggedValues.select(PathElement.sequenceElement(),
+                                         PathElement.groupElement("value"));
  * }</pre></blockquote>
  *
  * And, we can also replace the layout named {@code value} with another layout, as follows:
  * <blockquote><pre>{@code
-MemoryLayout newSeq = seq.map(l -> MemoryLayout.ofPadding(32), PathElement.sequenceElement(), PathElement.groupElement("value"));
+MemoryLayout taggedValuesWithHole = taggedValues.map(l -> MemoryLayout.ofPadding(32),
+                                            PathElement.sequenceElement(), PathElement.groupElement("value"));
  * }</pre></blockquote>
  *
  * That is, the above declaration is identical to the following, more verbose one:
  * <blockquote><pre>{@code
-MemoryLayout newSeq = MemoryLayout.ofSequence(5,
+MemoryLayout taggedValuesWithHole = MemoryLayout.ofSequence(5,
     MemoryLayout.ofStruct(
-        MemoryLayout.ofPaddingBits(32),
-        MemoryLayout.ofPaddingBits(32)
-));
- * }</pre></blockquote>
- *
- * Similarly, we can select the member layout named {@code value}, as follows:
- * <blockquote><pre>{@code
-MemoryLayout value = seq.select(PathElement.sequenceElement(), PathElement.groupElement("value"));
- * }</pre></blockquote>
- *
- * And, we can also replace the layout named {@code value} with another layout, as follows:
- * <blockquote><pre>{@code
-MemoryLayout newSeq = seq.map(l -> MemoryLayout.ofPadding(32), PathElement.sequenceElement(), PathElement.groupElement("value"));
- * }</pre></blockquote>
- *
- * That is, the above declaration is identical to the following, more verbose one:
- * <blockquote><pre>{@code
-MemoryLayout newSeq = MemoryLayout.ofSequence(5,
-    MemoryLayout.ofStruct(
+        MemoryLayout.ofValueBits(8, ByteOrder.NATIVE_ORDER).withName("kind").
         MemoryLayout.ofPaddingBits(32),
         MemoryLayout.ofPaddingBits(32)
 ));
@@ -161,11 +159,14 @@
  * This is important when obtaining memory access var handle from layouts, as in the following code:
  *
  * <blockquote><pre>{@code
-VarHandle valueHandle = seq.map(int.class, PathElement.sequenceElement(), PathElement.groupElement("value"));
+VarHandle valueHandle = taggedValues.varHandle(int.class,
+                                               PathElement.sequenceElement(),
+                                               PathElement.groupElement("value"));
  * }</pre></blockquote>
  *
- * Since the layout path {@code seq} constructed in the above example features exactly one free dimension,
- * it follows that the memory access var handle {@code valueHandle} will feature an extra {@code long}
+ * Since the layout path constructed in the above example features exactly one free dimension (as it doesn't specify
+ * <em>which</em> member layout named {@code value} should be selected from the enclosing sequence layout),
+ * it follows that the memory access var handle {@code valueHandle} will feature an <em>additional</em> {@code long}
  * access coordinate.
  *
  * <h2>Layout attributes</h2>
@@ -180,7 +181,7 @@
  * explicitly permitted types.
  *
  * @implSpec
- * Implementations of this class are immutable and thread-safe.
+ * Implementations of this interface are immutable, thread-safe and <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>.
  */
 public interface MemoryLayout extends Constable {
 
--- a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemorySegment.java	Tue Jun 23 10:09:26 2020 +0100
+++ b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemorySegment.java	Tue Jun 23 11:49:48 2020 +0100
@@ -44,7 +44,7 @@
 /**
  * A memory segment models a contiguous region of memory. A memory segment is associated with both spatial
  * and temporal bounds. Spatial bounds ensure that memory access operations on a memory segment cannot affect a memory location
- * which falls <em>outside</em> the boundaries of the memory segment being accessed. Temporal checks ensure that memory access
+ * which falls <em>outside</em> the boundaries of the memory segment being accessed. Temporal bounds ensure that memory access
  * operations on a segment cannot occur after a memory segment has been closed (see {@link MemorySegment#close()}).
  * <p>
  * All implementations of this interface must be <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>;
@@ -76,12 +76,22 @@
  * Finally, it is also possible to obtain a memory segment backed by a memory-mapped file using the factory method
  * {@link MemorySegment#mapFromPath(Path, long, long, FileChannel.MapMode)}. Such memory segments are called <em>mapped memory segments</em>
  * (see {@link MappedMemorySegment}).
+ * <p>
+ * Array and buffer segments are effectively <em>views</em> over existing memory regions which might outlive the
+ * lifecycle of the segments derived from them, and can even be manipulated directly (e.g. via array access, or direct use
+ * of the {@link ByteBuffer} API) by other clients. As a result, while sharing array or buffer segments is possible,
+ * it is strongly advised that clients wishing to do so take extra precautions to make sure that the underlying memory sources
+ * associated with such segments remain inaccessible, and that said memory sources are never aliased by more than one segment
+ * at a time - e.g. so as to prevent concurrent modifications of the contents of an array, or buffer segment.
  *
  * <h2>Closing a memory segment</h2>
  *
- * Memory segments are closed explicitly (see {@link MemorySegment#close()}). In general when a segment is closed, all off-heap
- * resources associated with it are released; this has different meanings depending on the kind of memory segment being
- * considered:
+ * Memory segments are closed explicitly (see {@link MemorySegment#close()}). When a segment is closed, it is no longer
+ * <em>alive</em> (see {@link #isAlive()}, and subsequent operation on the segment (or on any {@link MemoryAddress} instance
+ * derived from it) will fail with {@link IllegalStateException}.
+ * <p>
+ * Closing a segment might trigger the releasing of the underlying memory resources associated with said segment, depending on
+ * the kind of memory segment being considered:
  * <ul>
  *     <li>closing a native memory segment results in <em>freeing</em> the native memory associated with it</li>
  *     <li>closing a mapped memory segment results in the backing memory-mapped file to be unmapped</li>
@@ -92,32 +102,6 @@
  *     objects.</li>
  * </ul>
  *
- * <h2><a id = "thread-confinement">Thread confinement</a></h2>
- *
- * Memory segments support strong thread-confinement guarantees. Upon creation, they are assigned an <em>owner thread</em>,
- * typically the thread which initiated the creation operation. After creation, only the owner thread will be allowed
- * to directly manipulate the memory segment (e.g. close the memory segment) or access the underlying memory associated with
- * the segment using a memory access var handle. Any attempt to perform such operations from a thread other than the
- * owner thread will result in a runtime failure.
- * <p>
- * Memory segments support <em>serial thread confinement</em>; that is, ownership of a memory segment can change (see
- * {@link #withOwnerThread(Thread)}). This allows, for instance, for two threads {@code A} and {@code B} to share
- * a segment in a controlled, cooperative and race-free fashion.
- * <p>
- * In some cases, it might be useful for multiple threads to process the contents of the same memory segment concurrently
- * (e.g. in the case of parallel processing); while memory segments provide strong confinement guarantees, it is possible
- * to obtain a {@link Spliterator} from a segment, which can be used to slice the segment and allow multiple thread to
- * work in parallel on disjoint segment slices (this assumes that the access mode {@link #ACQUIRE} is set).
- * For instance, the following code can be used to sum all int values in a memory segment in parallel:
- * <blockquote><pre>{@code
-MemorySegment segment = ...
-SequenceLayout SEQUENCE_LAYOUT = MemoryLayout.ofSequence(1024, MemoryLayouts.JAVA_INT);
-VarHandle VH_int = SEQUENCE_LAYOUT.elementLayout().varHandle(int.class);
-int sum = StreamSupport.stream(MemorySegment.spliterator(segment, SEQUENCE_LAYOUT), true)
-            .mapToInt(s -> (int)VH_int.get(s.baseAddress()))
-            .sum();
- * }</pre></blockquote>
- *
  * <h2><a id = "access-modes">Access modes</a></h2>
  *
  * Memory segments supports zero or more <em>access modes</em>. Supported access modes are {@link #READ},
@@ -152,12 +136,38 @@
  * {@link ByteBuffer} API, but need to operate on large memory segments. Byte buffers obtained in such a way support
  * the same spatial and temporal access restrictions associated to the memory segment from which they originated.
  *
+ * <h2><a id = "thread-confinement">Thread confinement</a></h2>
+ *
+ * Memory segments support strong thread-confinement guarantees. Upon creation, they are assigned an <em>owner thread</em>,
+ * typically the thread which initiated the creation operation. After creation, only the owner thread will be allowed
+ * to directly manipulate the memory segment (e.g. close the memory segment) or access the underlying memory associated with
+ * the segment using a memory access var handle. Any attempt to perform such operations from a thread other than the
+ * owner thread will result in a runtime failure.
+ * <p>
+ * Memory segments support <em>serial thread confinement</em>; that is, ownership of a memory segment can change (see
+ * {@link #withOwnerThread(Thread)}). This allows, for instance, for two threads {@code A} and {@code B} to share
+ * a segment in a controlled, cooperative and race-free fashion.
+ * <p>
+ * In some cases, it might be useful for multiple threads to process the contents of the same memory segment concurrently
+ * (e.g. in the case of parallel processing); while memory segments provide strong confinement guarantees, it is possible
+ * to obtain a {@link Spliterator} from a segment, which can be used to slice the segment and allow multiple thread to
+ * work in parallel on disjoint segment slices (this assumes that the access mode {@link #ACQUIRE} is set).
+ * For instance, the following code can be used to sum all int values in a memory segment in parallel:
+ * <blockquote><pre>{@code
+MemorySegment segment = ...
+SequenceLayout SEQUENCE_LAYOUT = MemoryLayout.ofSequence(1024, MemoryLayouts.JAVA_INT);
+VarHandle VH_int = SEQUENCE_LAYOUT.elementLayout().varHandle(int.class);
+int sum = StreamSupport.stream(MemorySegment.spliterator(segment, SEQUENCE_LAYOUT), true)
+                       .mapToInt(s -> (int)VH_int.get(s.baseAddress()))
+                       .sum();
+ * }</pre></blockquote>
+ *
  * @apiNote In the future, if the Java language permits, {@link MemorySegment}
  * may become a {@code sealed} interface, which would prohibit subclassing except by
  * {@link MappedMemorySegment} and other explicitly permitted subtypes.
  *
  * @implSpec
- * Implementations of this interface are immutable and thread-safe.
+ * Implementations of this interface are immutable, thread-safe and <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>.
  */
 public interface MemorySegment extends AutoCloseable {
 
@@ -276,8 +286,8 @@
 
     /**
      * Closes this memory segment. Once a memory segment has been closed, any attempt to use the memory segment,
-     * or to access the memory associated with the segment will fail with {@link IllegalStateException}. Depending on
-     * the kind of memory segment being closed, calling this method further trigger deallocation of all the resources
+     * or to access any {@link MemoryAddress} instance associated with it will fail with {@link IllegalStateException}.
+     * Depending on the kind of memory segment being closed, calling this method further triggers deallocation of all the resources
      * associated with the memory segment.
      * @throws IllegalStateException if this segment is not <em>alive</em>, or if access occurs from a thread other than the
      * thread owning this segment, or if the segment cannot be closed because it is being operated upon by a different
@@ -610,8 +620,8 @@
      * (often as a plain {@code long} value). The segment will feature all <a href="#access-modes">access modes</a>
      * (see {@link #ALL_ACCESS}).
      * <p>
-     * This method is <em>restricted</em>. 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
+     * This method is <em>restricted</em>. Restricted methods are unsafe, and, if used incorrectly, their use might crash
+     * the JVM or, worse, silently result in memory corruption. Thus, clients should refrain from depending on
      * restricted methods, and use safe and supported functionalities, where possible.
      *
      * @param addr the desired base address
--- a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/package-info.java	Tue Jun 23 10:09:26 2020 +0100
+++ b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/package-info.java	Tue Jun 23 11:49:48 2020 +0100
@@ -25,29 +25,7 @@
  */
 
 /**
- * <p> Classes to support low-level, safe and efficient memory access. For example:
- *
- * <pre>{@code
-static final VarHandle intHandle = MemoryHandles.varHandle(int.class, ByteOrder.BIG_ENDIAN);
-
-try (MemorySegment segment = MemorySegment.allocateNative(10 * 4)) {
-   MemoryAddress base = segment.baseAddress();
-   for (long i = 0 ; i < 10 ; i++) {
-     intHandle.set(base.addOffset(i * 4), (int)i);
-   }
- }
- * }</pre>
- *
- * Here we create a var handle, namely {@code intHandle}, to manipulate values of the primitive type {@code int}, at
- * a given memory location. We then create a <em>native</em> memory segment, that is, a memory segment backed by
- * off-heap memory; the size of the segment is 40 bytes, enough to store 10 values of the primitive type {@code int}.
- * The segment is created inside a <em>try-with-resources</em> construct: this idiom ensures that all the memory resources
- * associated with the segment will be released at the end of the block, according to the semantics described in
- * Section {@jls 14.20.3} of <cite>The Java&trade; Language Specification</cite>. Inside the try-with-resources block, we initialize
- * the contents of the memory segment; more specifically, if we view the memory segment as a set of 10 adjacent slots,
- * {@code s[i]}, where {@code 0 <= i < 10}, where the size of each slot is exactly 4 bytes, the initialization logic above will set each slot
- * so that {@code s[i] = i}, again where {@code 0 <= i < 10}.
- *
+ * <p> Classes to support low-level, safe and efficient memory access.
  * <p>
  * The key abstractions introduced by this package are {@link jdk.incubator.foreign.MemorySegment} and {@link jdk.incubator.foreign.MemoryAddress}.
  * The first models a contiguous memory region, which can reside either inside or outside the Java heap; the latter models an address - which can
@@ -57,6 +35,32 @@
  * layout, obtain its alignment requirements, and so on. Memory layouts also provide an alternate, more abstract way, to produce
  * memory access var handles, e.g. using <a href="MemoryLayout.html#layout-paths"><em>layout paths</em></a>.
  *
+ * For example, to allocate an off-heap memory region big enough to hold 10 values of the primitive type {@code int}, and fill it with values
+ * ranging from {@code 0} to {@code 9}, we can use the following code:
+ *
+ * <pre>{@code
+static final VarHandle intHandle = MemoryHandles.varHandle(int.class, ByteOrder.nativeOrder());
+
+try (MemorySegment segment = MemorySegment.allocateNative(10 * 4)) {
+    MemoryAddress base = segment.baseAddress();
+    for (long i = 0 ; i < 10 ; i++) {
+       intHandle.set(base.addOffset(i * 4), (int)i);
+    }
+}
+ * }</pre>
+ *
+ * Here we create a var handle, namely {@code intHandle}, to manipulate values of the primitive type {@code int}, at
+ * a given memory location. Also, {@code intHandle} is stored in a {@code static} and {@code final} field, to achieve
+ * better performance and allow for inlining of the memory access operation through the {@link java.lang.invoke.VarHandle}
+ * instance. We then create a <em>native</em> memory segment, that is, a memory segment backed by
+ * off-heap memory; the size of the segment is 40 bytes, enough to store 10 values of the primitive type {@code int}.
+ * The segment is created inside a <em>try-with-resources</em> construct: this idiom ensures that all the memory resources
+ * associated with the segment will be released at the end of the block, according to the semantics described in
+ * Section {@jls 14.20.3} of <cite>The Java&trade; Language Specification</cite>. Inside the try-with-resources block, we initialize
+ * the contents of the memory segment; more specifically, if we view the memory segment as a set of 10 adjacent slots,
+ * {@code s[i]}, where {@code 0 <= i < 10}, where the size of each slot is exactly 4 bytes, the initialization logic above will set each slot
+ * so that {@code s[i] = i}, again where {@code 0 <= i < 10}.
+ *
  * <h2><a id="deallocation"></a>Deterministic deallocation</h2>
  *
  * When writing code that manipulates memory segments, especially if backed by memory which resides outside the Java heap, it is
--- a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/MemoryAddressImpl.java	Tue Jun 23 10:09:26 2020 +0100
+++ b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/MemoryAddressImpl.java	Tue Jun 23 11:49:48 2020 +0100
@@ -54,17 +54,6 @@
         this.offset = offset;
     }
 
-    public static void copy(MemoryAddressImpl src, MemoryAddressImpl dst, long size) {
-        src.checkAccess(0, size, true);
-        dst.checkAccess(0, size, false);
-        //check disjoint
-        long offsetSrc = src.unsafeGetOffset();
-        long offsetDst = dst.unsafeGetOffset();
-        Object baseSrc = src.unsafeGetBase();
-        Object baseDst = dst.unsafeGetBase();
-        UNSAFE.copyMemory(baseSrc, offsetSrc, baseDst, offsetDst, size);
-    }
-
     // MemoryAddress methods
 
     @Override