Skip to content
This repository has been archived by the owner. It is now read-only.
Permalink
Browse files
8248011: Improve javadoc of Foreign Memory Access API
Reviewed-by: psandoz
  • Loading branch information
mcimadamore committed Jun 23, 2020
1 parent 7f69acc commit 36d716aa086ffaa2f0db62dcf883e210abf1526a
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 101 deletions.
@@ -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 {
/**
@@ -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:
* <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:
* 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
long valueOffset = seq.bitOffset(PathElement.sequenceElement(), PathElement.groupElement("value"));
* }</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.ofPaddingBits(32),
MemoryLayout.ofPaddingBits(32)
));
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.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 {

@@ -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 @@ static <S extends MemorySegment> Spliterator<S> spliterator(S segment, SequenceL

/**
* 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 @@ static MemorySegment allocateNative(long bytesSize, long alignmentBytes) {
* (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
@@ -25,21 +25,34 @@
*/

/**
* <p> Classes to support low-level, safe and efficient memory access. For example:
* <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
* sometimes be expressed as an offset into a given segment. A memory address represents the main access coordinate of a memory access var handle, which can be obtained
* using the combinator methods defined in the {@link jdk.incubator.foreign.MemoryHandles} class. Finally, the {@link jdk.incubator.foreign.MemoryLayout} class
* hierarchy enables description of <em>memory layouts</em> and basic operations such as computing the size in bytes of a given
* 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.BIG_ENDIAN);
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);
}
}
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
* 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
@@ -48,15 +61,6 @@
* {@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>
* 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
* sometimes be expressed as an offset into a given segment. A memory address represents the main access coordinate of a memory access var handle, which can be obtained
* using the combinator methods defined in the {@link jdk.incubator.foreign.MemoryHandles} class. Finally, the {@link jdk.incubator.foreign.MemoryLayout} class
* hierarchy enables description of <em>memory layouts</em> and basic operations such as computing the size in bytes of a given
* 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>.
*
* <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

0 comments on commit 36d716a

Please sign in to comment.