diff --git a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryAddress.java b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryAddress.java index dfd85559063..f5774d79eb7 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryAddress.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryAddress.java @@ -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 value-based. */ public interface MemoryAddress { /** diff --git a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryLayout.java b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryLayout.java index 4c3efa16668..b9a6d145f39 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryLayout.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryLayout.java @@ -54,6 +54,27 @@ import java.util.stream.Stream; * element layout (see {@link SequenceLayout}); a group layout denotes an aggregation of (typically) heterogeneous * member layouts (see {@link GroupLayout}). *

+ * For instance, consider the following struct declaration in C: + * + *

{@code
+ typedef struct {
+     char kind;
+     int value;
+ } TaggedValues[5];
+ * }
+ * + * The above declaration can be modelled using a layout object, as follows: + * + *
{@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");
+ * }
+ *

* All implementations of this interface must be value-based; * 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...)}). *

* Such layout paths 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 value in the first sequence element, as follows: *

{@code
-SequenceLayout seq = MemoryLayout.ofSequence(5,
-    MemoryLayout.ofStruct(
-        MemoryLayout.ofPaddingBits(32),
-        MemoryLayout.ofValueBits(32, ByteOrder.BIG_ENDIAN).withName("value")
-));
- * }
- * - * We can obtain the offset, in bits, of the member layout named value from seq, as follows: - *
{@code
-long valueOffset = seq.bitOffset(PathElement.sequenceElement(), PathElement.groupElement("value"));
+long valueOffset = taggedValues.bitOffset(PathElement.sequenceElement(0),
+                                          PathElement.groupElement("value")); // yields 32
  * }
* * Similarly, we can select the member layout named {@code value}, as follows: *
{@code
-MemoryLayout value = seq.select(PathElement.sequenceElement(), PathElement.groupElement("value"));
+MemoryLayout value = taggedValues.select(PathElement.sequenceElement(),
+                                         PathElement.groupElement("value"));
  * }
* * And, we can also replace the layout named {@code value} with another layout, as follows: *
{@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"));
  * }
* * That is, the above declaration is identical to the following, more verbose one: *
{@code
-MemoryLayout newSeq = MemoryLayout.ofSequence(5,
-    MemoryLayout.ofStruct(
-        MemoryLayout.ofPaddingBits(32),
-        MemoryLayout.ofPaddingBits(32)
-));
- * }
- * - * Similarly, we can select the member layout named {@code value}, as follows: - *
{@code
-MemoryLayout value = seq.select(PathElement.sequenceElement(), PathElement.groupElement("value"));
- * }
- * - * And, we can also replace the layout named {@code value} with another layout, as follows: - *
{@code
-MemoryLayout newSeq = seq.map(l -> MemoryLayout.ofPadding(32), PathElement.sequenceElement(), PathElement.groupElement("value"));
- * }
- * - * That is, the above declaration is identical to the following, more verbose one: - *
{@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:
  *
  * 
{@code
-VarHandle valueHandle = seq.map(int.class, PathElement.sequenceElement(), PathElement.groupElement("value"));
+VarHandle valueHandle = taggedValues.varHandle(int.class,
+                                               PathElement.sequenceElement(),
+                                               PathElement.groupElement("value"));
  * }
* - * 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 + * which 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 additional {@code long} * access coordinate. * *

Layout attributes

@@ -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 value-based. */ public interface MemoryLayout extends Constable { diff --git a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemorySegment.java b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemorySegment.java index d15ade978f7..239d14b8f5e 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemorySegment.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemorySegment.java @@ -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 outside the boundaries of the memory segment being accessed. Temporal checks ensure that memory access + * which falls outside 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()}). *

* All implementations of this interface must be value-based; @@ -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 mapped memory segments * (see {@link MappedMemorySegment}). + *

+ * Array and buffer segments are effectively views 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. * *

Closing a memory segment

* - * 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 + * alive (see {@link #isAlive()}, and subsequent operation on the segment (or on any {@link MemoryAddress} instance + * derived from it) will fail with {@link IllegalStateException}. + *

+ * 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: *

* - *

Thread confinement

- * - * Memory segments support strong thread-confinement guarantees. Upon creation, they are assigned an owner thread, - * 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. - *

- * Memory segments support serial thread confinement; 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. - *

- * 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: - *

{@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();
- * }
- * *

Access modes

* * Memory segments supports zero or more access modes. 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. * + *

Thread confinement

+ * + * Memory segments support strong thread-confinement guarantees. Upon creation, they are assigned an owner thread, + * 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. + *

+ * Memory segments support serial thread confinement; 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. + *

+ * 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: + *

{@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();
+ * }
+ * * @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 value-based. */ 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 alive, 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 access modes * (see {@link #ALL_ACCESS}). *

- * 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 + * This method is restricted. 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 diff --git a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/package-info.java b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/package-info.java index 33990e517b0..1d8408db2e5 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/package-info.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/package-info.java @@ -25,29 +25,7 @@ */ /** - *

Classes to support low-level, safe and efficient memory access. For example: - * - *

{@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);
-   }
- }
- * }
- * - * 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 native 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 try-with-resources 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 The Java™ Language Specification. 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}. - * + *

Classes to support low-level, safe and efficient memory access. *

* 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 layout paths. * + * 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: + * + *

{@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);
+    }
+}
+ * }
+ * + * 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 native 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 try-with-resources 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 The Java™ Language Specification. 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}. + * *

Deterministic deallocation

* * When writing code that manipulates memory segments, especially if backed by memory which resides outside the Java heap, it is diff --git a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/MemoryAddressImpl.java b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/MemoryAddressImpl.java index 4430eb53920..feed358562d 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/MemoryAddressImpl.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/MemoryAddressImpl.java @@ -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