mirror of
https://github.com/openjdk/jdk.git
synced 2026-01-28 03:58:21 +00:00
8248011: Improve javadoc of Foreign Memory Access API
Reviewed-by: psandoz
This commit is contained in:
parent
7f69acc778
commit
36d716aa08
@ -52,7 +52,7 @@ import jdk.internal.foreign.MemoryAddressImpl;
|
||||
* 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 @@ import java.util.stream.Stream;
|
||||
* 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 @@ import java.util.stream.Stream;
|
||||
* 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.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 taggedValuesWithHole = MemoryLayout.ofSequence(5,
|
||||
MemoryLayout.ofStruct(
|
||||
MemoryLayout.ofValueBits(8, ByteOrder.NATIVE_ORDER).withName("kind").
|
||||
MemoryLayout.ofPaddingBits(32),
|
||||
MemoryLayout.ofPaddingBits(32)
|
||||
));
|
||||
@ -161,11 +159,14 @@ MemoryLayout newSeq = MemoryLayout.ofSequence(5,
|
||||
* 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 @@ VarHandle valueHandle = seq.map(int.class, PathElement.sequenceElement(), PathEl
|
||||
* 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 @@ import java.util.function.Consumer;
|
||||
/**
|
||||
* 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 @@ import java.util.function.Consumer;
|
||||
* 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 @@ import java.util.function.Consumer;
|
||||
* 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 @@ MemorySegment roSegment = segment.withAccessModes(segment.accessModes() & ~WRITE
|
||||
* {@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 @@ public interface MemorySegment extends AutoCloseable {
|
||||
|
||||
/**
|
||||
* 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 @@ allocateNative(bytesSize, 1);
|
||||
* (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,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™ 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 @@ try (MemorySegment segment = MemorySegment.allocateNative(10 * 4)) {
|
||||
* 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™ 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
|
||||
|
||||
@ -54,17 +54,6 @@ public final class MemoryAddressImpl implements MemoryAddress, MemoryAddressProx
|
||||
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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user