Skip to content

Commit

Permalink
Initial push
Browse files Browse the repository at this point in the history
  • Loading branch information
mcimadamore committed Oct 13, 2023
1 parent 7d31146 commit c463103
Show file tree
Hide file tree
Showing 11 changed files with 132 additions and 64 deletions.
20 changes: 10 additions & 10 deletions src/java.base/share/classes/java/lang/foreign/Arena.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,17 +43,19 @@
* to obtain native segments.
* <p>
* 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
* accessible and their backing regions of memory are never deallocated. Moreover, memory segments allocated with the
* global arena can be {@linkplain MemorySegment#isAccessibleBy(Thread) accessed} from any thread.
* features an <em>unbounded lifetime</em>. The scope of the global arena is the global scope.
* As such, native segments allocated with the global arena are associated are always accessible and their backing regions
* 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:
* MemorySegment segment = Arena.global().allocate(100, 1); // @highlight regex='global()'
* ...
* // segment is never deallocated!
*}
* <p>
* 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
* <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:
Expand All @@ -67,11 +69,9 @@
* 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,
* 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
* arena is created, and ends when the confined arena is {@linkplain #close() closed}. As a result, the regions of memory
* backing memory segments allocated with a confined arena are deallocated when the confined arena is closed.
* When this happens, all the segments allocated with the confined arena are invalidated, and subsequent access
* operations on these segments will fail {@link IllegalStateException}:
* bounded lifetimes that are managed manually. For instance, when a confined arena is {@linkplain #close() closed}
* successfully, its scope is {@linkplain Scope#isAlive() invalidated}. As a result, all the memory segments allocated
* by the arena can no longer be accessed, and their regions of memory are deallocated:
*
* {@snippet lang = java:
* MemorySegment segment = null;
Expand Down Expand Up @@ -219,7 +219,7 @@ static Arena ofAuto() {
*/
static Arena global() {
class Holder {
static final Arena GLOBAL = MemorySessionImpl.GLOBAL.asArena();
static final Arena GLOBAL = MemorySessionImpl.GLOBAL_SESSION.asArena();
}
return Holder.GLOBAL;
}
Expand Down
6 changes: 3 additions & 3 deletions src/java.base/share/classes/java/lang/foreign/Linker.java
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@
*
* 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
* 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
* 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.
Expand Down Expand Up @@ -563,7 +563,7 @@ static Linker nativeLinker() {
* <p>
* 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
* 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}
* {@code T}, then the size of the returned segment is set to {@code T.byteSize()}.
* <p>
Expand Down Expand Up @@ -602,7 +602,7 @@ static Linker nativeLinker() {
* upcall stub segment will be deallocated when the provided confined arena is {@linkplain Arena#close() closed}.
* <p>
* 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}.
* 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()}.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -460,7 +460,7 @@ default MethodHandle byteOffsetHandle(PathElement... elements) {
* </ul>
* <p>
* 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 a scope that is
* always alive. Moreover, the size of the segment depends on whether the address layout has a
* {@linkplain AddressLayout#targetLayout() target layout}. More specifically:
* <ul>
Expand Down
71 changes: 48 additions & 23 deletions src/java.base/share/classes/java/lang/foreign/MemorySegment.java
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,7 @@
* 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
* (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>
* </ul>
* <p>
Expand Down Expand Up @@ -635,7 +635,7 @@ default MemorySegment asSlice(long offset, MemoryLayout layout) {
* MemorySegment cleanupSegment = MemorySegment.ofAddress(this.address())
* .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()}.
* <p>
* This method is <a href="package-summary.html#restricted"><em>restricted</em></a>.
Expand Down Expand Up @@ -674,7 +674,7 @@ default MemorySegment asSlice(long offset, MemoryLayout layout) {
* MemorySegment cleanupSegment = MemorySegment.ofAddress(this.address())
* .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}.
* <p>
* This method is <a href="package-summary.html#restricted"><em>restricted</em></a>.
Expand Down Expand Up @@ -1183,11 +1183,9 @@ default void setString(long offset, String str, Charset charset) {
* <p>
* 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
* same scope {@code S}. Otherwise, the scope of the returned segment is a fresh scope that is always alive.
* <p>
* The scope associated with the returned segment keeps the provided buffer reachable. As such, if
* 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.
* same scope {@code S}. Otherwise, the scope of the returned segment is an automatic scope that keeps the provided
* buffer reachable. As such, if 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.
* @return a memory segment, derived from the given buffer instance.
Expand All @@ -1202,7 +1200,7 @@ static MemorySegment ofBuffer(Buffer buffer) {

/**
* 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.
*
* @param byteArray the primitive array backing the heap memory segment.
Expand All @@ -1214,7 +1212,7 @@ static MemorySegment ofArray(byte[] byteArray) {

/**
* 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.
*
* @param charArray the primitive array backing the heap segment.
Expand All @@ -1226,7 +1224,7 @@ static MemorySegment ofArray(char[] charArray) {

/**
* 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.
*
* @param shortArray the primitive array backing the heap segment.
Expand All @@ -1238,7 +1236,7 @@ static MemorySegment ofArray(short[] shortArray) {

/**
* 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.
*
* @param intArray the primitive array backing the heap segment.
Expand All @@ -1250,7 +1248,7 @@ static MemorySegment ofArray(int[] intArray) {

/**
* 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.
*
* @param floatArray the primitive array backing the heap segment.
Expand All @@ -1262,7 +1260,7 @@ static MemorySegment ofArray(float[] floatArray) {

/**
* 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.
*
* @param longArray the primitive array backing the heap segment.
Expand All @@ -1274,7 +1272,7 @@ static MemorySegment ofArray(long[] longArray) {

/**
* 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.
*
* @param doubleArray the primitive array backing the heap segment.
Expand All @@ -1285,13 +1283,13 @@ static MemorySegment ofArray(double[] doubleArray) {
}

/**
* A zero-length native segment modelling the {@code NULL} address.
* A zero-length native segment modelling the {@code NULL} address. Equivalent to {@code MemorySegment.ofAddress(0L)}.
*/
MemorySegment NULL = new NativeMemorySegmentImpl();

/**
* 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>
* 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}
Expand Down Expand Up @@ -1718,7 +1716,7 @@ default void set(ValueLayout.OfDouble layout, long offset, double value) {

/**
* 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
* {@linkplain AddressLayout#targetLayout() target layout} {@code T}, then the size of the returned segment
* is set to {@code T.byteSize()}.
Expand Down Expand Up @@ -2157,7 +2155,7 @@ default void setAtIndex(ValueLayout.OfDouble layout, long index, double value) {

/**
* 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
* {@linkplain AddressLayout#targetLayout() target layout} {@code T}, then the size of the returned segment
* is set to {@code T.byteSize()}.
Expand Down Expand Up @@ -2365,11 +2363,38 @@ static long mismatch(MemorySegment srcSegment, long srcFromOffset, long srcToOff

/**
* 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
* obtained indirectly, by creating a new {@linkplain Arena arena}.
* cannot be accessed if its associated scope is not {@linkplain #isAlive() alive}. Scope instances can be compared
* for equality. That is, two scopes are considered {@linkplain #equals(Object)} if they denote the same lifetime.
* <p>
* The lifetime of a memory segment can be either <em>unbounded</em> or <em>bounded</em>. An unbounded 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>
* Scope instances can be compared for equality. That is, two scopes
* are considered {@linkplain #equals(Object)} if they denote the same lifetime.
* 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 {
/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ default SymbolLookup or(SymbolLookup other) {
* <p>
* 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
* 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
* (and their symbols available) as long as a loader lookup for that class loader, or any of the segments
* obtained by it, is reachable.
Expand All @@ -189,7 +189,7 @@ static SymbolLookup loaderLookup() {
if ((loader == null || loader instanceof BuiltinClassLoader)) {
loaderArena = Arena.global();
} else {
MemorySessionImpl session = MemorySessionImpl.heapSession(loader);
MemorySessionImpl session = MemorySessionImpl.createHeap(loader);
loaderArena = session.asArena();
}
return name -> {
Expand Down
Loading

0 comments on commit c463103

Please sign in to comment.