mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 06:45:07 +02:00
8317819: Scope should reflect lifetime of underying resource (mainline)
Reviewed-by: jvernee
This commit is contained in:
parent
6f1d8962df
commit
b07da3ae15
12 changed files with 254 additions and 64 deletions
|
@ -43,9 +43,10 @@ import java.lang.foreign.MemorySegment.Scope;
|
||||||
* to obtain native segments.
|
* to obtain native segments.
|
||||||
* <p>
|
* <p>
|
||||||
* The simplest arena is the {@linkplain Arena#global() global arena}. The global arena
|
* The simplest arena is the {@linkplain Arena#global() global arena}. The global arena
|
||||||
* features an <em>unbounded lifetime</em>. As such, native segments allocated with the global arena are always
|
* features an <em>unbounded lifetime</em>. The scope of the global arena is the global scope.
|
||||||
* accessible and their backing regions of memory are never deallocated. Moreover, memory segments allocated with the
|
* As such, native segments allocated with the global arena are always accessible and their backing regions
|
||||||
* global arena can be {@linkplain MemorySegment#isAccessibleBy(Thread) accessed} from any thread.
|
* of memory are never deallocated.
|
||||||
|
* Moreover, memory segments allocated with the global arena can be {@linkplain MemorySegment#isAccessibleBy(Thread) accessed} from any thread.
|
||||||
* {@snippet lang = java:
|
* {@snippet lang = java:
|
||||||
* MemorySegment segment = Arena.global().allocate(100, 1); // @highlight regex='global()'
|
* MemorySegment segment = Arena.global().allocate(100, 1); // @highlight regex='global()'
|
||||||
* ...
|
* ...
|
||||||
|
@ -53,7 +54,8 @@ import java.lang.foreign.MemorySegment.Scope;
|
||||||
*}
|
*}
|
||||||
* <p>
|
* <p>
|
||||||
* Alternatively, clients can obtain an {@linkplain Arena#ofAuto() automatic arena}, that is an arena
|
* Alternatively, clients can obtain an {@linkplain Arena#ofAuto() automatic arena}, that is an arena
|
||||||
* which features a <em>bounded lifetime</em> that is managed, automatically, by the garbage collector. As such, the regions
|
* which features a <em>bounded lifetime</em> that is managed, automatically, by the garbage collector. The scope
|
||||||
|
* of an automatic arena is an automatic scope. As such, the regions
|
||||||
* of memory backing memory segments allocated with the automatic arena are deallocated at some unspecified time
|
* of memory backing memory segments allocated with the automatic arena are deallocated at some unspecified time
|
||||||
* <em>after</em> the automatic arena (and all the segments allocated by it) becomes
|
* <em>after</em> the automatic arena (and all the segments allocated by it) becomes
|
||||||
* <a href="../../../java/lang/ref/package.html#reachability">unreachable</a>, as shown below:
|
* <a href="../../../java/lang/ref/package.html#reachability">unreachable</a>, as shown below:
|
||||||
|
@ -67,11 +69,9 @@ import java.lang.foreign.MemorySegment.Scope;
|
||||||
* Rather than leaving deallocation in the hands of the Java runtime, clients will often wish to exercise control over
|
* Rather than leaving deallocation in the hands of the Java runtime, clients will often wish to exercise control over
|
||||||
* the timing of deallocation for regions of memory that back memory segments. Two kinds of arenas support this,
|
* the timing of deallocation for regions of memory that back memory segments. Two kinds of arenas support this,
|
||||||
* namely {@linkplain #ofConfined() confined} and {@linkplain #ofShared() shared} arenas. They both feature
|
* namely {@linkplain #ofConfined() confined} and {@linkplain #ofShared() shared} arenas. They both feature
|
||||||
* bounded lifetimes that are managed manually. For instance, the lifetime of a confined arena starts when the confined
|
* bounded lifetimes that are managed manually. For instance, when a confined arena is {@linkplain #close() closed}
|
||||||
* arena is created, and ends when the confined arena is {@linkplain #close() closed}. As a result, the regions of memory
|
* successfully, its scope is {@linkplain Scope#isAlive() invalidated}. As a result, all the memory segments allocated
|
||||||
* backing memory segments allocated with a confined arena are deallocated when the confined arena is closed.
|
* by the arena can no longer be accessed, and their regions of memory are deallocated:
|
||||||
* When this happens, all the segments allocated with the confined arena are invalidated, and subsequent access
|
|
||||||
* operations on these segments will fail {@link IllegalStateException}:
|
|
||||||
*
|
*
|
||||||
* {@snippet lang = java:
|
* {@snippet lang = java:
|
||||||
* MemorySegment segment = null;
|
* MemorySegment segment = null;
|
||||||
|
@ -219,7 +219,7 @@ public interface Arena extends SegmentAllocator, AutoCloseable {
|
||||||
*/
|
*/
|
||||||
static Arena global() {
|
static Arena global() {
|
||||||
class Holder {
|
class Holder {
|
||||||
static final Arena GLOBAL = MemorySessionImpl.GLOBAL.asArena();
|
static final Arena GLOBAL = MemorySessionImpl.GLOBAL_SESSION.asArena();
|
||||||
}
|
}
|
||||||
return Holder.GLOBAL;
|
return Holder.GLOBAL;
|
||||||
}
|
}
|
||||||
|
|
|
@ -365,7 +365,7 @@ import java.util.stream.Stream;
|
||||||
*
|
*
|
||||||
* The size of the segment returned by the {@code malloc} downcall method handle is
|
* The size of the segment returned by the {@code malloc} downcall method handle is
|
||||||
* <a href="MemorySegment.html#wrapping-addresses">zero</a>. Moreover, the scope of the
|
* <a href="MemorySegment.html#wrapping-addresses">zero</a>. Moreover, the scope of the
|
||||||
* returned segment is a fresh scope that is always alive. To provide safe access to the segment, we must,
|
* returned segment is the global scope. To provide safe access to the segment, we must,
|
||||||
* unsafely, resize the segment to the desired size (100, in this case). It might also be desirable to
|
* unsafely, resize the segment to the desired size (100, in this case). It might also be desirable to
|
||||||
* attach the segment to some existing {@linkplain Arena arena}, so that the lifetime of the region of memory
|
* attach the segment to some existing {@linkplain Arena arena}, so that the lifetime of the region of memory
|
||||||
* backing the segment can be managed automatically, as for any other native segment created directly from Java code.
|
* backing the segment can be managed automatically, as for any other native segment created directly from Java code.
|
||||||
|
@ -563,7 +563,7 @@ public sealed interface Linker permits AbstractLinker {
|
||||||
* <p>
|
* <p>
|
||||||
* Moreover, if the provided function descriptor's return layout is an {@linkplain AddressLayout address layout},
|
* Moreover, if the provided function descriptor's return layout is an {@linkplain AddressLayout address layout},
|
||||||
* invoking the returned method handle will return a native segment associated with
|
* invoking the returned method handle will return a native segment associated with
|
||||||
* a fresh scope that is always alive. Under normal conditions, the size of the returned segment is {@code 0}.
|
* the global scope. Under normal conditions, the size of the returned segment is {@code 0}.
|
||||||
* However, if the function descriptor's return layout has a {@linkplain AddressLayout#targetLayout() target layout}
|
* However, if the function descriptor's return layout has a {@linkplain AddressLayout#targetLayout() target layout}
|
||||||
* {@code T}, then the size of the returned segment is set to {@code T.byteSize()}.
|
* {@code T}, then the size of the returned segment is set to {@code T.byteSize()}.
|
||||||
* <p>
|
* <p>
|
||||||
|
@ -602,7 +602,7 @@ public sealed interface Linker permits AbstractLinker {
|
||||||
* upcall stub segment will be deallocated when the provided confined arena is {@linkplain Arena#close() closed}.
|
* upcall stub segment will be deallocated when the provided confined arena is {@linkplain Arena#close() closed}.
|
||||||
* <p>
|
* <p>
|
||||||
* An upcall stub argument whose corresponding layout is an {@linkplain AddressLayout address layout}
|
* An upcall stub argument whose corresponding layout is an {@linkplain AddressLayout address layout}
|
||||||
* is a native segment associated with a fresh scope that is always alive.
|
* is a native segment associated with the global scope.
|
||||||
* Under normal conditions, the size of this segment argument is {@code 0}.
|
* Under normal conditions, the size of this segment argument is {@code 0}.
|
||||||
* However, if the address layout has a {@linkplain AddressLayout#targetLayout() target layout} {@code T}, then the size of the
|
* However, if the address layout has a {@linkplain AddressLayout#targetLayout() target layout} {@code T}, then the size of the
|
||||||
* segment argument is set to {@code T.byteSize()}.
|
* segment argument is set to {@code T.byteSize()}.
|
||||||
|
|
|
@ -492,8 +492,8 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin
|
||||||
* </ul>
|
* </ul>
|
||||||
* <p>
|
* <p>
|
||||||
* If the selected layout is an {@linkplain AddressLayout address layout}, calling {@link VarHandle#get(Object...)}
|
* If the selected layout is an {@linkplain AddressLayout address layout}, calling {@link VarHandle#get(Object...)}
|
||||||
* on the returned var handle will return a new memory segment. The segment is associated with a fresh scope that is
|
* on the returned var handle will return a new memory segment. The segment is associated with the global scope.
|
||||||
* always alive. Moreover, the size of the segment depends on whether the address layout has a
|
* Moreover, the size of the segment depends on whether the address layout has a
|
||||||
* {@linkplain AddressLayout#targetLayout() target layout}. More specifically:
|
* {@linkplain AddressLayout#targetLayout() target layout}. More specifically:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>If the address layout has a target layout {@code T}, then the size of the returned segment
|
* <li>If the address layout has a target layout {@code T}, then the size of the returned segment
|
||||||
|
@ -513,7 +513,7 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin
|
||||||
* {@snippet lang = "java":
|
* {@snippet lang = "java":
|
||||||
* VarHandle baseHandle = this.varHandle(P);
|
* VarHandle baseHandle = this.varHandle(P);
|
||||||
* MemoryLayout target = ((AddressLayout)this.select(P)).targetLayout().get();
|
* MemoryLayout target = ((AddressLayout)this.select(P)).targetLayout().get();
|
||||||
* VarHandle targetHandle = target.varHandle(P');
|
* VarHandle targetHandle = target.varHandle(P);
|
||||||
* targetHandle = MethodHandles.insertCoordinates(targetHandle, 1, 0L); // always access nested targets at offset 0
|
* targetHandle = MethodHandles.insertCoordinates(targetHandle, 1, 0L); // always access nested targets at offset 0
|
||||||
* targetHandle = MethodHandles.collectCoordinates(targetHandle, 0,
|
* targetHandle = MethodHandles.collectCoordinates(targetHandle, 0,
|
||||||
* baseHandle.toMethodHandle(VarHandle.AccessMode.GET));
|
* baseHandle.toMethodHandle(VarHandle.AccessMode.GET));
|
||||||
|
|
|
@ -376,7 +376,7 @@ import jdk.internal.vm.annotation.ForceInline;
|
||||||
* of memory whose size is not known, any access operations involving these segments cannot be validated.
|
* of memory whose size is not known, any access operations involving these segments cannot be validated.
|
||||||
* In effect, a zero-length memory segment <em>wraps</em> an address, and it cannot be used without explicit intent
|
* In effect, a zero-length memory segment <em>wraps</em> an address, and it cannot be used without explicit intent
|
||||||
* (see below);</li>
|
* (see below);</li>
|
||||||
* <li>The segment is associated with a fresh scope that is always alive. Thus, while zero-length
|
* <li>The segment is associated with the global scope. Thus, while zero-length
|
||||||
* memory segments cannot be accessed directly, they can be passed, opaquely, to other pointer-accepting foreign functions.</li>
|
* memory segments cannot be accessed directly, they can be passed, opaquely, to other pointer-accepting foreign functions.</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
* <p>
|
* <p>
|
||||||
|
@ -625,7 +625,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl {
|
||||||
* MemorySegment cleanupSegment = MemorySegment.ofAddress(this.address())
|
* MemorySegment cleanupSegment = MemorySegment.ofAddress(this.address())
|
||||||
* .reinterpret(byteSize());
|
* .reinterpret(byteSize());
|
||||||
* }
|
* }
|
||||||
* That is, the cleanup action receives a segment that is associated with a fresh scope that is always alive,
|
* That is, the cleanup action receives a segment that is associated with the global scope,
|
||||||
* and is accessible from any thread. The size of the segment accepted by the cleanup action is {@link #byteSize()}.
|
* and is accessible from any thread. The size of the segment accepted by the cleanup action is {@link #byteSize()}.
|
||||||
* <p>
|
* <p>
|
||||||
* This method is <a href="package-summary.html#restricted"><em>restricted</em></a>.
|
* This method is <a href="package-summary.html#restricted"><em>restricted</em></a>.
|
||||||
|
@ -664,7 +664,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl {
|
||||||
* MemorySegment cleanupSegment = MemorySegment.ofAddress(this.address())
|
* MemorySegment cleanupSegment = MemorySegment.ofAddress(this.address())
|
||||||
* .reinterpret(newSize);
|
* .reinterpret(newSize);
|
||||||
* }
|
* }
|
||||||
* That is, the cleanup action receives a segment that is associated with a fresh scope that is always alive,
|
* That is, the cleanup action receives a segment that is associated with the global scope,
|
||||||
* and is accessible from any thread. The size of the segment accepted by the cleanup action is {@code newSize}.
|
* and is accessible from any thread. The size of the segment accepted by the cleanup action is {@code newSize}.
|
||||||
* <p>
|
* <p>
|
||||||
* This method is <a href="package-summary.html#restricted"><em>restricted</em></a>.
|
* This method is <a href="package-summary.html#restricted"><em>restricted</em></a>.
|
||||||
|
@ -1155,11 +1155,9 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl {
|
||||||
* <p>
|
* <p>
|
||||||
* If the provided buffer has been obtained by calling {@link #asByteBuffer()} on a memory segment whose
|
* If the provided buffer has been obtained by calling {@link #asByteBuffer()} on a memory segment whose
|
||||||
* {@linkplain Scope scope} is {@code S}, the returned segment will be associated with the
|
* {@linkplain Scope scope} is {@code S}, the returned segment will be associated with the
|
||||||
* same scope {@code S}. Otherwise, the scope of the returned segment is a fresh scope that is always alive.
|
* same scope {@code S}. Otherwise, the scope of the returned segment is an automatic scope that keeps the provided
|
||||||
* <p>
|
* buffer reachable. As such, if the provided buffer is a direct buffer, its backing memory region will not be
|
||||||
* The scope associated with the returned segment keeps the provided buffer reachable. As such, if
|
* deallocated as long as the returned segment (or any of its slices) are kept reachable.
|
||||||
* the provided buffer is a direct buffer, its backing memory region will not be deallocated as long as the
|
|
||||||
* returned segment (or any of its slices) are kept reachable.
|
|
||||||
*
|
*
|
||||||
* @param buffer the buffer instance to be turned into a new memory segment.
|
* @param buffer the buffer instance to be turned into a new memory segment.
|
||||||
* @return a memory segment, derived from the given buffer instance.
|
* @return a memory segment, derived from the given buffer instance.
|
||||||
|
@ -1174,7 +1172,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a heap segment backed by the on-heap region of memory that holds the given byte array.
|
* Creates a heap segment backed by the on-heap region of memory that holds the given byte array.
|
||||||
* The scope of the returned segment is a fresh scope that is always alive, and keeps the given array reachable.
|
* The scope of the returned segment is an automatic scope that keeps the given array reachable.
|
||||||
* The returned segment is always accessible, from any thread. Its {@link #address()} is set to zero.
|
* The returned segment is always accessible, from any thread. Its {@link #address()} is set to zero.
|
||||||
*
|
*
|
||||||
* @param byteArray the primitive array backing the heap memory segment.
|
* @param byteArray the primitive array backing the heap memory segment.
|
||||||
|
@ -1186,7 +1184,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a heap segment backed by the on-heap region of memory that holds the given char array.
|
* Creates a heap segment backed by the on-heap region of memory that holds the given char array.
|
||||||
* The scope of the returned segment is a fresh scope that is always alive, and keeps the given array reachable.
|
* The scope of the returned segment is an automatic scope that keeps the given array reachable.
|
||||||
* The returned segment is always accessible, from any thread. Its {@link #address()} is set to zero.
|
* The returned segment is always accessible, from any thread. Its {@link #address()} is set to zero.
|
||||||
*
|
*
|
||||||
* @param charArray the primitive array backing the heap segment.
|
* @param charArray the primitive array backing the heap segment.
|
||||||
|
@ -1198,7 +1196,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a heap segment backed by the on-heap region of memory that holds the given short array.
|
* Creates a heap segment backed by the on-heap region of memory that holds the given short array.
|
||||||
* The scope of the returned segment is a fresh scope that is always alive, and keeps the given array reachable.
|
* The scope of the returned segment is an automatic scope that keeps the given array reachable.
|
||||||
* The returned segment is always accessible, from any thread. Its {@link #address()} is set to zero.
|
* The returned segment is always accessible, from any thread. Its {@link #address()} is set to zero.
|
||||||
*
|
*
|
||||||
* @param shortArray the primitive array backing the heap segment.
|
* @param shortArray the primitive array backing the heap segment.
|
||||||
|
@ -1210,7 +1208,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a heap segment backed by the on-heap region of memory that holds the given int array.
|
* Creates a heap segment backed by the on-heap region of memory that holds the given int array.
|
||||||
* The scope of the returned segment is a fresh scope that is always alive, and keeps the given array reachable.
|
* The scope of the returned segment is an automatic scope that keeps the given array reachable.
|
||||||
* The returned segment is always accessible, from any thread. Its {@link #address()} is set to zero.
|
* The returned segment is always accessible, from any thread. Its {@link #address()} is set to zero.
|
||||||
*
|
*
|
||||||
* @param intArray the primitive array backing the heap segment.
|
* @param intArray the primitive array backing the heap segment.
|
||||||
|
@ -1222,7 +1220,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a heap segment backed by the on-heap region of memory that holds the given float array.
|
* Creates a heap segment backed by the on-heap region of memory that holds the given float array.
|
||||||
* The scope of the returned segment is a fresh scope that is always alive, and keeps the given array reachable.
|
* The scope of the returned segment is an automatic scope that keeps the given array reachable.
|
||||||
* The returned segment is always accessible, from any thread. Its {@link #address()} is set to zero.
|
* The returned segment is always accessible, from any thread. Its {@link #address()} is set to zero.
|
||||||
*
|
*
|
||||||
* @param floatArray the primitive array backing the heap segment.
|
* @param floatArray the primitive array backing the heap segment.
|
||||||
|
@ -1234,7 +1232,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a heap segment backed by the on-heap region of memory that holds the given long array.
|
* Creates a heap segment backed by the on-heap region of memory that holds the given long array.
|
||||||
* The scope of the returned segment is a fresh scope that is always alive, and keeps the given array reachable.
|
* The scope of the returned segment is an automatic scope that keeps the given array reachable.
|
||||||
* The returned segment is always accessible, from any thread. Its {@link #address()} is set to zero.
|
* The returned segment is always accessible, from any thread. Its {@link #address()} is set to zero.
|
||||||
*
|
*
|
||||||
* @param longArray the primitive array backing the heap segment.
|
* @param longArray the primitive array backing the heap segment.
|
||||||
|
@ -1246,7 +1244,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a heap segment backed by the on-heap region of memory that holds the given double array.
|
* Creates a heap segment backed by the on-heap region of memory that holds the given double array.
|
||||||
* The scope of the returned segment is a fresh scope that is always alive, and keeps the given array reachable.
|
* The scope of the returned segment is an automatic scope that keeps the given array reachable.
|
||||||
* The returned segment is always accessible, from any thread. Its {@link #address()} is set to zero.
|
* The returned segment is always accessible, from any thread. Its {@link #address()} is set to zero.
|
||||||
*
|
*
|
||||||
* @param doubleArray the primitive array backing the heap segment.
|
* @param doubleArray the primitive array backing the heap segment.
|
||||||
|
@ -1263,7 +1261,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a zero-length native segment from the given {@linkplain #address() address value}.
|
* Creates a zero-length native segment from the given {@linkplain #address() address value}.
|
||||||
* The returned segment is associated with a scope that is always alive, and is accessible from any thread.
|
* The returned segment is associated with the global scope, and is accessible from any thread.
|
||||||
* <p>
|
* <p>
|
||||||
* On 32-bit platforms, the given address value will be normalized such that the
|
* On 32-bit platforms, the given address value will be normalized such that the
|
||||||
* highest-order ("leftmost") 32 bits of the {@link MemorySegment#address() address}
|
* highest-order ("leftmost") 32 bits of the {@link MemorySegment#address() address}
|
||||||
|
@ -1642,7 +1640,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads an address from this segment at the given offset, with the given layout. The read address is wrapped in
|
* Reads an address from this segment at the given offset, with the given layout. The read address is wrapped in
|
||||||
* a native segment, associated with a fresh scope that is always alive. Under normal conditions,
|
* a native segment, associated with the global scope. Under normal conditions,
|
||||||
* the size of the returned segment is {@code 0}. However, if the provided address layout has a
|
* the size of the returned segment is {@code 0}. However, if the provided address layout has a
|
||||||
* {@linkplain AddressLayout#targetLayout() target layout} {@code T}, then the size of the returned segment
|
* {@linkplain AddressLayout#targetLayout() target layout} {@code T}, then the size of the returned segment
|
||||||
* is set to {@code T.byteSize()}.
|
* is set to {@code T.byteSize()}.
|
||||||
|
@ -1994,7 +1992,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads an address from this segment at the given at the given index, scaled by the given layout size. The read address is wrapped in
|
* Reads an address from this segment at the given at the given index, scaled by the given layout size. The read address is wrapped in
|
||||||
* a native segment, associated with a fresh scope that is always alive. Under normal conditions,
|
* a native segment, associated with the global scope. Under normal conditions,
|
||||||
* the size of the returned segment is {@code 0}. However, if the provided address layout has a
|
* the size of the returned segment is {@code 0}. However, if the provided address layout has a
|
||||||
* {@linkplain AddressLayout#targetLayout() target layout} {@code T}, then the size of the returned segment
|
* {@linkplain AddressLayout#targetLayout() target layout} {@code T}, then the size of the returned segment
|
||||||
* is set to {@code T.byteSize()}.
|
* is set to {@code T.byteSize()}.
|
||||||
|
@ -2192,11 +2190,38 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A scope models the <em>lifetime</em> of all the memory segments associated with it. That is, a memory segment
|
* A scope models the <em>lifetime</em> of all the memory segments associated with it. That is, a memory segment
|
||||||
* cannot be accessed if its associated scope is not {@linkplain #isAlive() alive}. A new scope is typically
|
* cannot be accessed if its associated scope is not {@linkplain #isAlive() alive}. Scope instances can be compared
|
||||||
* obtained indirectly, by creating a new {@linkplain Arena arena}.
|
* for equality. That is, two scopes are considered {@linkplain #equals(Object) equal} if they denote the same lifetime.
|
||||||
* <p>
|
* <p>
|
||||||
* Scope instances can be compared for equality. That is, two scopes
|
* The lifetime of a memory segment can be either <em>unbounded</em> or <em>bounded</em>. An unbounded lifetime
|
||||||
* are considered {@linkplain #equals(Object)} if they denote the same lifetime.
|
* is modelled with the <em>global scope</em>. The global scope is always {@link #isAlive() alive}. As such, a segment
|
||||||
|
* associated with the global scope features trivial temporal bounds, and is always accessible.
|
||||||
|
* Segments associated with the global scope are:
|
||||||
|
* <ul>
|
||||||
|
* <li>Segments obtained from the {@linkplain Arena#global() global arena};</li>
|
||||||
|
* <li>Segments obtained from a raw address, using the {@link MemorySegment#ofAddress(long)} factory; and</li>
|
||||||
|
* <li><a href="#wrapping-addresses">Zero-length memory segments.</a></li>
|
||||||
|
* </ul>
|
||||||
|
* <p>
|
||||||
|
* Conversely, a bounded lifetime is modelled with a segment scope that can be invalidated, either {@link Arena#close() explicitly},
|
||||||
|
* or automatically, by the garbage collector. A segment scope that is invalidated automatically is an <em>automatic scope</em>.
|
||||||
|
* An automatic scope is always {@link #isAlive() alive} as long as it is <a href="../../../java/lang/ref/package.html#reachability">reachable</a>.
|
||||||
|
* Segments associated with an automatic scope are:
|
||||||
|
* <ul>
|
||||||
|
* <li>Segments obtained from an {@linkplain Arena#ofAuto() automatic arena};</li>
|
||||||
|
* <li>Segments obtained from a Java array, e.g. using the {@link MemorySegment#ofArray(int[])} factory;</li>
|
||||||
|
* <li>Segments obtained from a buffer, using the {@link MemorySegment#ofBuffer(Buffer)} factory; and</li>
|
||||||
|
* <li>Segments obtained from {@linkplain SymbolLookup#loaderLookup() loader lookup}.</li>
|
||||||
|
* </ul>
|
||||||
|
* If two memory segments are obtained from the same {@linkplain #ofBuffer(Buffer) buffer}
|
||||||
|
* or {@linkplain #ofArray(int[]) array}, the automatic scopes associated with said segments are considered
|
||||||
|
* {@linkplain #equals(Object) equal}, as the two segments have the same lifetime:
|
||||||
|
* {@snippet lang=java :
|
||||||
|
* byte[] arr = new byte[10];
|
||||||
|
* MemorySegment segment1 = MemorySegment.ofArray(arr);
|
||||||
|
* MemorySegment segment2 = MemorySegment.ofArray(arr);
|
||||||
|
* assert segment1.scope().equals(segment2.scope());
|
||||||
|
* }
|
||||||
*/
|
*/
|
||||||
sealed interface Scope permits MemorySessionImpl {
|
sealed interface Scope permits MemorySessionImpl {
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -165,7 +165,7 @@ public interface SymbolLookup {
|
||||||
* <p>
|
* <p>
|
||||||
* Libraries associated with a class loader are unloaded when the class loader becomes
|
* Libraries associated with a class loader are unloaded when the class loader becomes
|
||||||
* <a href="../../../java/lang/ref/package.html#reachability">unreachable</a>. The symbol lookup
|
* <a href="../../../java/lang/ref/package.html#reachability">unreachable</a>. The symbol lookup
|
||||||
* returned by this method is associated with a fresh {@linkplain MemorySegment.Scope scope} which keeps the caller's
|
* returned by this method is associated with an automatic {@linkplain MemorySegment.Scope scope} which keeps the caller's
|
||||||
* class loader reachable. Therefore, libraries associated with the caller's class loader are kept loaded
|
* class loader reachable. Therefore, libraries associated with the caller's class loader are kept loaded
|
||||||
* (and their symbols available) as long as a loader lookup for that class loader, or any of the segments
|
* (and their symbols available) as long as a loader lookup for that class loader, or any of the segments
|
||||||
* obtained by it, is reachable.
|
* obtained by it, is reachable.
|
||||||
|
@ -189,7 +189,7 @@ public interface SymbolLookup {
|
||||||
if ((loader == null || loader instanceof BuiltinClassLoader)) {
|
if ((loader == null || loader instanceof BuiltinClassLoader)) {
|
||||||
loaderArena = Arena.global();
|
loaderArena = Arena.global();
|
||||||
} else {
|
} else {
|
||||||
MemorySessionImpl session = MemorySessionImpl.heapSession(loader);
|
MemorySessionImpl session = MemorySessionImpl.createHeap(loader);
|
||||||
loaderArena = session.asArena();
|
loaderArena = session.asArena();
|
||||||
}
|
}
|
||||||
return name -> {
|
return name -> {
|
||||||
|
|
|
@ -55,6 +55,7 @@ import jdk.internal.reflect.Reflection;
|
||||||
import jdk.internal.util.ArraysSupport;
|
import jdk.internal.util.ArraysSupport;
|
||||||
import jdk.internal.util.Preconditions;
|
import jdk.internal.util.Preconditions;
|
||||||
import jdk.internal.vm.annotation.ForceInline;
|
import jdk.internal.vm.annotation.ForceInline;
|
||||||
|
import sun.nio.ch.DirectBuffer;
|
||||||
|
|
||||||
import static java.lang.foreign.ValueLayout.JAVA_BYTE;
|
import static java.lang.foreign.ValueLayout.JAVA_BYTE;
|
||||||
|
|
||||||
|
@ -556,7 +557,7 @@ public abstract sealed class AbstractMemorySegmentImpl
|
||||||
if (bufferSegment != null) {
|
if (bufferSegment != null) {
|
||||||
bufferScope = bufferSegment.scope;
|
bufferScope = bufferSegment.scope;
|
||||||
} else {
|
} else {
|
||||||
bufferScope = MemorySessionImpl.heapSession(bb);
|
bufferScope = MemorySessionImpl.createHeap(bufferRef(bb));
|
||||||
}
|
}
|
||||||
if (base != null) {
|
if (base != null) {
|
||||||
if (base instanceof byte[]) {
|
if (base instanceof byte[]) {
|
||||||
|
@ -584,6 +585,17 @@ public abstract sealed class AbstractMemorySegmentImpl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Object bufferRef(Buffer buffer) {
|
||||||
|
if (buffer instanceof DirectBuffer directBuffer) {
|
||||||
|
// direct buffer, return either the buffer attachment (for slices and views), or the buffer itself
|
||||||
|
return directBuffer.attachment() != null ?
|
||||||
|
directBuffer.attachment() : directBuffer;
|
||||||
|
} else {
|
||||||
|
// heap buffer, return the underlying array
|
||||||
|
return NIO_ACCESS.getBufferBase(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static int getScaleFactor(Buffer buffer) {
|
private static int getScaleFactor(Buffer buffer) {
|
||||||
if (buffer instanceof ByteBuffer) {
|
if (buffer instanceof ByteBuffer) {
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -25,20 +25,23 @@
|
||||||
|
|
||||||
package jdk.internal.foreign;
|
package jdk.internal.foreign;
|
||||||
|
|
||||||
|
import jdk.internal.access.JavaNioAccess;
|
||||||
|
import jdk.internal.access.SharedSecrets;
|
||||||
import jdk.internal.vm.annotation.ForceInline;
|
import jdk.internal.vm.annotation.ForceInline;
|
||||||
|
import sun.nio.ch.DirectBuffer;
|
||||||
|
|
||||||
|
import java.nio.Buffer;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The global, non-closeable, shared session. Similar to a shared session, but its {@link #close()} method throws unconditionally.
|
* The global, non-closeable, shared session. Similar to a shared session, but its {@link #close()} method throws unconditionally.
|
||||||
* Adding new resources to the global session, does nothing: as the session can never become not-alive, there is nothing to track.
|
* Adding new resources to the global session, does nothing: as the session can never become not-alive, there is nothing to track.
|
||||||
* Acquiring and or releasing a memory session similarly does nothing.
|
* Acquiring and or releasing a memory session similarly does nothing.
|
||||||
*/
|
*/
|
||||||
final class GlobalSession extends MemorySessionImpl {
|
non-sealed class GlobalSession extends MemorySessionImpl {
|
||||||
|
|
||||||
final Object ref;
|
public GlobalSession() {
|
||||||
|
|
||||||
public GlobalSession(Object ref) {
|
|
||||||
super(null, null);
|
super(null, null);
|
||||||
this.ref = ref;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -67,4 +70,32 @@ final class GlobalSession extends MemorySessionImpl {
|
||||||
public void justClose() {
|
public void justClose() {
|
||||||
throw nonCloseable();
|
throw nonCloseable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a global session that wraps a heap object. Possible objects are: Java arrays, buffers and
|
||||||
|
* class loaders. Objects of two heap sessions are compared by identity. That is, if the wrapped object is the same,
|
||||||
|
* then the resulting heap sessions are also considered equals. We do not compare the objects using
|
||||||
|
* {@link Object#equals(Object)}, as that would be problematic when comparing buffers, whose equality and
|
||||||
|
* hash codes are content-dependent.
|
||||||
|
*/
|
||||||
|
static class HeapSession extends GlobalSession {
|
||||||
|
|
||||||
|
final Object ref;
|
||||||
|
|
||||||
|
public HeapSession(Object ref) {
|
||||||
|
super();
|
||||||
|
this.ref = Objects.requireNonNull(ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
return obj instanceof HeapSession session &&
|
||||||
|
ref == session.ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return System.identityHashCode(ref);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,8 +48,7 @@ final class MappedMemorySegmentImpl extends NativeMemorySegmentImpl {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
ByteBuffer makeByteBuffer() {
|
ByteBuffer makeByteBuffer() {
|
||||||
return NIO_ACCESS.newMappedByteBuffer(unmapper, min, (int)length, null,
|
return NIO_ACCESS.newMappedByteBuffer(unmapper, min, (int)length, null, this);
|
||||||
scope == MemorySessionImpl.GLOBAL ? null : this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -33,6 +33,8 @@ import java.lang.invoke.MethodHandles;
|
||||||
import java.lang.invoke.VarHandle;
|
import java.lang.invoke.VarHandle;
|
||||||
import java.lang.ref.Cleaner;
|
import java.lang.ref.Cleaner;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import jdk.internal.foreign.GlobalSession.HeapSession;
|
||||||
import jdk.internal.misc.ScopedMemoryAccess;
|
import jdk.internal.misc.ScopedMemoryAccess;
|
||||||
import jdk.internal.vm.annotation.ForceInline;
|
import jdk.internal.vm.annotation.ForceInline;
|
||||||
|
|
||||||
|
@ -59,10 +61,10 @@ public abstract sealed class MemorySessionImpl
|
||||||
static final VarHandle STATE;
|
static final VarHandle STATE;
|
||||||
static final int MAX_FORKS = Integer.MAX_VALUE;
|
static final int MAX_FORKS = Integer.MAX_VALUE;
|
||||||
|
|
||||||
public static final MemorySessionImpl GLOBAL = new GlobalSession(null);
|
|
||||||
|
|
||||||
static final ScopedMemoryAccess.ScopedAccessError ALREADY_CLOSED = new ScopedMemoryAccess.ScopedAccessError(MemorySessionImpl::alreadyClosed);
|
static final ScopedMemoryAccess.ScopedAccessError ALREADY_CLOSED = new ScopedMemoryAccess.ScopedAccessError(MemorySessionImpl::alreadyClosed);
|
||||||
static final ScopedMemoryAccess.ScopedAccessError WRONG_THREAD = new ScopedMemoryAccess.ScopedAccessError(MemorySessionImpl::wrongThread);
|
static final ScopedMemoryAccess.ScopedAccessError WRONG_THREAD = new ScopedMemoryAccess.ScopedAccessError(MemorySessionImpl::wrongThread);
|
||||||
|
// This is the session of all zero-length memory segments
|
||||||
|
public static final MemorySessionImpl GLOBAL_SESSION = new GlobalSession();
|
||||||
|
|
||||||
final ResourceList resourceList;
|
final ResourceList resourceList;
|
||||||
final Thread owner;
|
final Thread owner;
|
||||||
|
@ -143,6 +145,10 @@ public abstract sealed class MemorySessionImpl
|
||||||
return new ImplicitSession(cleaner);
|
return new ImplicitSession(cleaner);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static MemorySessionImpl createHeap(Object ref) {
|
||||||
|
return new HeapSession(ref);
|
||||||
|
}
|
||||||
|
|
||||||
public abstract void release0();
|
public abstract void release0();
|
||||||
|
|
||||||
public abstract void acquire0();
|
public abstract void acquire0();
|
||||||
|
@ -230,10 +236,6 @@ public abstract sealed class MemorySessionImpl
|
||||||
|
|
||||||
abstract void justClose();
|
abstract void justClose();
|
||||||
|
|
||||||
public static MemorySessionImpl heapSession(Object ref) {
|
|
||||||
return new GlobalSession(ref);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A list of all cleanup actions associated with a memory session. Cleanup actions are modelled as instances
|
* A list of all cleanup actions associated with a memory session. Cleanup actions are modelled as instances
|
||||||
* of the {@link ResourceCleanup} class, and, together, form a linked list. Depending on whether a session
|
* of the {@link ResourceCleanup} class, and, together, form a linked list. Depending on whether a session
|
||||||
|
|
|
@ -69,8 +69,7 @@ sealed class NativeMemorySegmentImpl extends AbstractMemorySegmentImpl permits M
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
ByteBuffer makeByteBuffer() {
|
ByteBuffer makeByteBuffer() {
|
||||||
return NIO_ACCESS.newDirectByteBuffer(min, (int) this.length, null,
|
return NIO_ACCESS.newDirectByteBuffer(min, (int) this.length, null, this);
|
||||||
scope == MemorySessionImpl.GLOBAL ? null : this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -77,7 +77,7 @@ public class SegmentFactories {
|
||||||
@ForceInline
|
@ForceInline
|
||||||
public static MemorySegment makeNativeSegmentUnchecked(long min, long byteSize) {
|
public static MemorySegment makeNativeSegmentUnchecked(long min, long byteSize) {
|
||||||
ensureInitialized();
|
ensureInitialized();
|
||||||
return new NativeMemorySegmentImpl(min, byteSize, false, new GlobalSession(null));
|
return new NativeMemorySegmentImpl(min, byteSize, false, MemorySessionImpl.GLOBAL_SESSION);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MemorySegment fromArray(byte[] arr) {
|
public static MemorySegment fromArray(byte[] arr) {
|
||||||
|
@ -85,7 +85,7 @@ public class SegmentFactories {
|
||||||
Objects.requireNonNull(arr);
|
Objects.requireNonNull(arr);
|
||||||
long byteSize = (long)arr.length * Unsafe.ARRAY_BYTE_INDEX_SCALE;
|
long byteSize = (long)arr.length * Unsafe.ARRAY_BYTE_INDEX_SCALE;
|
||||||
return new OfByte(Unsafe.ARRAY_BYTE_BASE_OFFSET, arr, byteSize, false,
|
return new OfByte(Unsafe.ARRAY_BYTE_BASE_OFFSET, arr, byteSize, false,
|
||||||
MemorySessionImpl.heapSession(arr));
|
MemorySessionImpl.createHeap(arr));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MemorySegment fromArray(short[] arr) {
|
public static MemorySegment fromArray(short[] arr) {
|
||||||
|
@ -93,7 +93,7 @@ public class SegmentFactories {
|
||||||
Objects.requireNonNull(arr);
|
Objects.requireNonNull(arr);
|
||||||
long byteSize = (long)arr.length * Unsafe.ARRAY_SHORT_INDEX_SCALE;
|
long byteSize = (long)arr.length * Unsafe.ARRAY_SHORT_INDEX_SCALE;
|
||||||
return new OfShort(Unsafe.ARRAY_SHORT_BASE_OFFSET, arr, byteSize, false,
|
return new OfShort(Unsafe.ARRAY_SHORT_BASE_OFFSET, arr, byteSize, false,
|
||||||
MemorySessionImpl.heapSession(arr));
|
MemorySessionImpl.createHeap(arr));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MemorySegment fromArray(int[] arr) {
|
public static MemorySegment fromArray(int[] arr) {
|
||||||
|
@ -101,7 +101,7 @@ public class SegmentFactories {
|
||||||
Objects.requireNonNull(arr);
|
Objects.requireNonNull(arr);
|
||||||
long byteSize = (long)arr.length * Unsafe.ARRAY_INT_INDEX_SCALE;
|
long byteSize = (long)arr.length * Unsafe.ARRAY_INT_INDEX_SCALE;
|
||||||
return new OfInt(Unsafe.ARRAY_INT_BASE_OFFSET, arr, byteSize, false,
|
return new OfInt(Unsafe.ARRAY_INT_BASE_OFFSET, arr, byteSize, false,
|
||||||
MemorySessionImpl.heapSession(arr));
|
MemorySessionImpl.createHeap(arr));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MemorySegment fromArray(char[] arr) {
|
public static MemorySegment fromArray(char[] arr) {
|
||||||
|
@ -109,7 +109,7 @@ public class SegmentFactories {
|
||||||
Objects.requireNonNull(arr);
|
Objects.requireNonNull(arr);
|
||||||
long byteSize = (long)arr.length * Unsafe.ARRAY_CHAR_INDEX_SCALE;
|
long byteSize = (long)arr.length * Unsafe.ARRAY_CHAR_INDEX_SCALE;
|
||||||
return new OfChar(Unsafe.ARRAY_CHAR_BASE_OFFSET, arr, byteSize, false,
|
return new OfChar(Unsafe.ARRAY_CHAR_BASE_OFFSET, arr, byteSize, false,
|
||||||
MemorySessionImpl.heapSession(arr));
|
MemorySessionImpl.createHeap(arr));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MemorySegment fromArray(float[] arr) {
|
public static MemorySegment fromArray(float[] arr) {
|
||||||
|
@ -117,7 +117,7 @@ public class SegmentFactories {
|
||||||
Objects.requireNonNull(arr);
|
Objects.requireNonNull(arr);
|
||||||
long byteSize = (long)arr.length * Unsafe.ARRAY_FLOAT_INDEX_SCALE;
|
long byteSize = (long)arr.length * Unsafe.ARRAY_FLOAT_INDEX_SCALE;
|
||||||
return new OfFloat(Unsafe.ARRAY_FLOAT_BASE_OFFSET, arr, byteSize, false,
|
return new OfFloat(Unsafe.ARRAY_FLOAT_BASE_OFFSET, arr, byteSize, false,
|
||||||
MemorySessionImpl.heapSession(arr));
|
MemorySessionImpl.createHeap(arr));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MemorySegment fromArray(double[] arr) {
|
public static MemorySegment fromArray(double[] arr) {
|
||||||
|
@ -125,7 +125,7 @@ public class SegmentFactories {
|
||||||
Objects.requireNonNull(arr);
|
Objects.requireNonNull(arr);
|
||||||
long byteSize = (long)arr.length * Unsafe.ARRAY_DOUBLE_INDEX_SCALE;
|
long byteSize = (long)arr.length * Unsafe.ARRAY_DOUBLE_INDEX_SCALE;
|
||||||
return new OfDouble(Unsafe.ARRAY_DOUBLE_BASE_OFFSET, arr, byteSize, false,
|
return new OfDouble(Unsafe.ARRAY_DOUBLE_BASE_OFFSET, arr, byteSize, false,
|
||||||
MemorySessionImpl.heapSession(arr));
|
MemorySessionImpl.createHeap(arr));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MemorySegment fromArray(long[] arr) {
|
public static MemorySegment fromArray(long[] arr) {
|
||||||
|
@ -133,7 +133,7 @@ public class SegmentFactories {
|
||||||
Objects.requireNonNull(arr);
|
Objects.requireNonNull(arr);
|
||||||
long byteSize = (long)arr.length * Unsafe.ARRAY_LONG_INDEX_SCALE;
|
long byteSize = (long)arr.length * Unsafe.ARRAY_LONG_INDEX_SCALE;
|
||||||
return new OfLong(Unsafe.ARRAY_LONG_BASE_OFFSET, arr, byteSize, false,
|
return new OfLong(Unsafe.ARRAY_LONG_BASE_OFFSET, arr, byteSize, false,
|
||||||
MemorySessionImpl.heapSession(arr));
|
MemorySessionImpl.createHeap(arr));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MemorySegment allocateSegment(long byteSize, long byteAlignment, MemorySessionImpl sessionImpl,
|
public static MemorySegment allocateSegment(long byteSize, long byteAlignment, MemorySessionImpl sessionImpl,
|
||||||
|
|
122
test/jdk/java/foreign/TestScope.java
Normal file
122
test/jdk/java/foreign/TestScope.java
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @test
|
||||||
|
* @run testng/othervm --enable-native-access=ALL-UNNAMED TestScope
|
||||||
|
*/
|
||||||
|
|
||||||
|
import org.testng.annotations.*;
|
||||||
|
|
||||||
|
import java.lang.foreign.Arena;
|
||||||
|
import java.lang.foreign.MemorySegment;
|
||||||
|
import java.lang.foreign.SymbolLookup;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.IntBuffer;
|
||||||
|
|
||||||
|
import static org.testng.Assert.*;
|
||||||
|
|
||||||
|
public class TestScope {
|
||||||
|
|
||||||
|
static {
|
||||||
|
System.loadLibrary("LookupTest");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDifferentArrayScope() {
|
||||||
|
MemorySegment.Scope scope1 = MemorySegment.ofArray(new byte[10]).scope();
|
||||||
|
MemorySegment.Scope scope2 = MemorySegment.ofArray(new byte[10]).scope();
|
||||||
|
assertNotEquals(scope1, scope2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDifferentBufferScope() {
|
||||||
|
MemorySegment.Scope scope1 = MemorySegment.ofBuffer(ByteBuffer.allocateDirect(10)).scope();
|
||||||
|
MemorySegment.Scope scope2 = MemorySegment.ofBuffer(ByteBuffer.allocateDirect(10)).scope();
|
||||||
|
assertNotEquals(scope1, scope2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDifferentArenaScope() {
|
||||||
|
MemorySegment.Scope scope1 = Arena.ofAuto().allocate(10).scope();
|
||||||
|
MemorySegment.Scope scope2 = Arena.ofAuto().allocate(10).scope();
|
||||||
|
assertNotEquals(scope1, scope2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSameArrayScope() {
|
||||||
|
byte[] arr = new byte[10];
|
||||||
|
assertEquals(MemorySegment.ofArray(arr).scope(), MemorySegment.ofArray(arr).scope());
|
||||||
|
ByteBuffer buf = ByteBuffer.wrap(arr);
|
||||||
|
assertEquals(MemorySegment.ofArray(arr).scope(), MemorySegment.ofBuffer(buf).scope());
|
||||||
|
testDerivedBufferScope(MemorySegment.ofArray(arr));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSameBufferScope() {
|
||||||
|
ByteBuffer buf = ByteBuffer.allocateDirect(10);
|
||||||
|
assertEquals(MemorySegment.ofBuffer(buf).scope(), MemorySegment.ofBuffer(buf).scope());
|
||||||
|
testDerivedBufferScope(MemorySegment.ofBuffer(buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSameArenaScope() {
|
||||||
|
try (Arena arena = Arena.ofConfined()) {
|
||||||
|
MemorySegment segment1 = arena.allocate(10);
|
||||||
|
MemorySegment segment2 = arena.allocate(10);
|
||||||
|
assertEquals(segment1.scope(), segment2.scope());
|
||||||
|
testDerivedBufferScope(segment1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSameNativeScope() {
|
||||||
|
MemorySegment segment1 = MemorySegment.ofAddress(42);
|
||||||
|
MemorySegment segment2 = MemorySegment.ofAddress(43);
|
||||||
|
assertEquals(segment1.scope(), segment2.scope());
|
||||||
|
assertEquals(segment1.scope(), segment2.reinterpret(10).scope());
|
||||||
|
assertEquals(segment1.scope(), Arena.global().scope());
|
||||||
|
testDerivedBufferScope(segment1.reinterpret(10));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSameLookupScope() {
|
||||||
|
SymbolLookup loaderLookup = SymbolLookup.loaderLookup();
|
||||||
|
MemorySegment segment1 = loaderLookup.find("f").get();
|
||||||
|
MemorySegment segment2 = loaderLookup.find("c").get();
|
||||||
|
assertEquals(segment1.scope(), segment2.scope());
|
||||||
|
testDerivedBufferScope(segment1.reinterpret(10));
|
||||||
|
}
|
||||||
|
|
||||||
|
void testDerivedBufferScope(MemorySegment segment) {
|
||||||
|
ByteBuffer buffer = segment.asByteBuffer();
|
||||||
|
MemorySegment.Scope expectedScope = segment.scope();
|
||||||
|
assertEquals(MemorySegment.ofBuffer(buffer).scope(), expectedScope);
|
||||||
|
// buffer slices should have same scope
|
||||||
|
ByteBuffer slice = buffer.slice(0, 2);
|
||||||
|
assertEquals(expectedScope, MemorySegment.ofBuffer(slice).scope());
|
||||||
|
// buffer views should have same scope
|
||||||
|
IntBuffer view = buffer.asIntBuffer();
|
||||||
|
assertEquals(expectedScope, MemorySegment.ofBuffer(view).scope());
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue